text: /******************************************************************************
   *
   * 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 <even dot rouault at mines-paris dot org>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included
   * in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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"
  
  CPL_CVSID("$Id$")
  
  GTMWaypointLayer::GTMWaypointLayer( const char* pszNameIn,
                                      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();
  
      pszName = CPLStrdup(pszNameIn);
  
      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 );
  }
  
  GTMWaypointLayer::~GTMWaypointLayer() {}
  
  /************************************************************************/
  /*                      WriteFeatureAttributes()                        */
  /************************************************************************/
  void GTMWaypointLayer::WriteFeatureAttributes( OGRFeature *poFeature, float altitude )
  {
      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->IsFieldSetAndNotNull( i ) )
          {
              const char* l_pszName = poFieldDefn->GetNameRef();
              /* Waypoint name */
              if (STARTS_WITH(l_pszName, "name"))
              {
                  strncpy (psNameField, poFeature->GetFieldAsString( i ), 10);
                  CPLStrlcat (psNameField, "          ", sizeof(psNameField));
              }
              /* Waypoint comment */
              else if (STARTS_WITH(l_pszName, "comment"))
              {
                  CPLFree(pszcomment);
                  pszcomment = CPLStrdup( poFeature->GetFieldAsString( i ) );
              }
              /* Waypoint icon */
              else if (STARTS_WITH(l_pszName, "icon"))
              {
                  icon = poFeature->GetFieldAsInteger( i );
                  // Check if it is a valid icon
                  if (icon < 1 || icon > 220)
                      icon = 48;
              }
              /* Waypoint date */
              else if (EQUAL(l_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( "" );
  
      const size_t commentLength = strlen(pszcomment);
  
      const size_t bufferSize = 27 + commentLength;
      void* pBuffer = CPLMalloc(bufferSize);
      void* 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();
  
      CPLFree(pszcomment);
      CPLFree(pBuffer);
  }
  
  /************************************************************************/
  /*                           ICreateFeature()                            */
  /************************************************************************/
  OGRErr GTMWaypointLayer::ICreateFeature (OGRFeature *poFeature)
  {
      VSILFILE* fp = poDS->getOutputFP();
      if (fp == NULL)
          return OGRERR_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,
                                   static_cast<float>(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();
  }
  