Logo Search packages:      
Sourcecode: magics++ version File versions  Download package

KMLDriver.cc

Go to the documentation of this file.
/******************************** LICENSE ********************************


 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.


 ******************************** LICENSE ********************************/

/*! \file KMLDriver.cc
    \brief Implementation of KMLDriver.
    \author Graphics Section, ECMWF

    Started: Thu Oct 18 18:41:52 2007

*/

#include <KMLDriver.h>
#include <Polyline.h>
#include <Text.h>
#include <Image.h>
#include <Symbol.h>
#include <Layer.h>

//! For generating KMZ files
extern "C"{
#include <sys/stat.h>
#include "minizip/zip.h"
00040 #define MAXFILENAME 256
#define WRITEBUFFERSIZE 16384
}


#ifdef MAGICS_RASTER
#include <gd.h>
#endif

#ifdef MAGICS_CAIRO
#include <cairo.h>
#endif

using namespace magics;

/*!
  \brief Constructor
*/
00058 KMLDriver::KMLDriver() : currentTimeBegin_(""),currentTimeEnd_(""),kml_placemark_(false),
                         polyline_begin_(true),polygon_begin_(true),MultiGeometrySet_(false),layer_(false),
                   render_(false),ecmwf_logo_(false)
{
      kmz_ = getKmz();
}

/*!
  \brief Destructor
*/
00068 KMLDriver::~KMLDriver()
{
}

/*!
  \brief Opening the driver
*/
00075 void KMLDriver::open()
{
      currentPage_ = 0;

      if(fileName_=="")
      {
            fileName_ = getFileName("kml");
      }
      if(kmz_) fileName_ = "doc.kml";

      pFile_.open(fileName_.c_str());
      if(!pFile_)
      {
            Log::fatal() << "KMLDriver::open() --> Cannot open KML output file: " << fileName_ << "!\n";
            exit(1);
      }
      pFile_      << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
            << "<kml xmlns=\"http://www.opengis.net/kml/2.2\" \n"
            << "     xmlns:atom=\"http://www.w3.org/2005/Atom\">\n"
            << "<Document>\n"<< " <name>"<<getTitle()<<"</name>\n"
            << " <open>1</open>\n";
      if(!getAuthor().empty()) pFile_ << " <atom:author><atom:name>"<<getAuthor()<<"</atom:name></atom:author>\n";
      if(!getLink().empty())   pFile_ << " <atom:link href=\""<<getLink()<<"\" />\n";
      pFile_      << " <description>\n"
            << " <![CDATA["<<getDescription()<<"]]>\n"
            << " </description>\n"
            << " <LookAt>\n"
            << "\t<longitude>"<<getLongitude()<<"</longitude>\n"
            << "\t<latitude>"<<getLatitude()<<"</latitude>\n"
            << "\t<range>"<<getRange()*1000.<<"</range>\n"
            << "\t<tilt>"<<getTilt()<<"</tilt>\n"
            << "\t<heading>0</heading>\n"    // always 0 = north!
            << "\t<altitudeMode>absolute</altitudeMode>\n"    // possibly "relativeToGround"
            << " </LookAt>\n"
            << " <Style id=\"check-hide-children\">\n"
            << "  <ListStyle>\n"
            << "   <listItemType>checkHideChildren</listItemType>\n"
            << "  </ListStyle>\n"
            << " </Style>\n";
      kml_placemark_=false;
}

/*!
  \brief Closing the driver

  While closing the driver all files generated (stored in kml_output_resource_list_ )
  are put into one KMZ zip like file. We use minizip (see minizip subdirectory to
  do so) since gnuzip is NOT sufficient!
*/
00124 void KMLDriver::close()
{
//    endPage();

      if (kml_placemark_) closePlacemark();

      if(ecmwf_logo_)
      {
         const string logofilename = "kml_logo.png";
         const string logofile = getEnvVariable("MAGPLUS_HOME") + MAGPLUS_PATH_TO_SHARE_ + logofilename;
         const string cmd = "cp " + logofile + " kml_logo.png";
         if(getDebug()) Log::dev()<< "KMLDriver.close() > Execute command: "<< cmd<< endl;
         int status = system(cmd.c_str());
         if(status)
         {
            Log::warning() << "KMLDriver.close() > Failed to copy logo file - Logo will be missing." << endl;
         }
         else
         {
            kml_output_resource_list_.push_back(logofilename);
            pFile_      << "<ScreenOverlay id=\"logo\">\n"
                  << "<name>Logo</name>\n"
                  << "<Icon>\n"
                  << " <href>"<<logofilename<<"</href>\n"
                  << "</Icon>\n"
                  << "<overlayXY x=\"0\" y=\"0\" xunits=\"fraction\" yunits=\"fraction\"/>\n"
                  << "<screenXY x=\"0\" y=\"0\" xunits=\"fraction\" yunits=\"fraction\"/>\n"
                  << "<size x=\"-1\" y=\"0.1\" xunits=\"fraction\" yunits=\"fraction\"/>\n"
                  << "</ScreenOverlay>\n";
         }
      }
      pFile_ << "</Document>\n</kml>\n";
      pFile_.close();
      kml_output_resource_list_.push_back(fileName_);

      if(kmz_ && !kml_output_resource_list_.empty())
      {
         fileName_ = getFileName("kmz");

         zipFile zf;
         int err=0;

         zf = zipOpen(fileName_.c_str(),0);
           if (zf == 0)
           {
               printf("ERROR opening zip file %s\n",fileName_.c_str());
               err= ZIP_ERRNO;
           }
           else
         {
            int size_buf = WRITEBUFFERSIZE;
            void* buf = (void*)malloc(size_buf);
            if (buf==0)
            {
                  Log::error() <<"Error allocating memory for KMZ generation!"<<endl;
                  return;
            }
            stringarray::iterator it    = kml_output_resource_list_.begin();
            stringarray::iterator itend = kml_output_resource_list_.end();
            for(; it != itend; it++)
            {
                  if(getDebug()) Log::dev()<< "KMLDriver.close() > Start adding file " <<  *it << " to KMZ file.\n";
                  FILE * fin;
                  int size_read;

                  const char *filename = (*it).c_str();

                  err = zipOpenNewFileInZip(zf,filename, 0, 0, 0, 0, 0, 0, Z_DEFLATED, Z_DEFAULT_COMPRESSION);

                  if(err != ZIP_OK)
                        Log::error() << "Could NOT open ... KMZ file!" << endl;
                  else
                  {
                    fin = fopen(filename,"rb");
                    if(fin==0)
                    {
                       Log::fatal() << "Open file "<<filename<<" to be added to KMZ FAILED!"<< endl;
                       return;
                    }
                    else
                    {
                      do{
                        err=ZIP_OK;
                        size_read = (int)fread(buf,1,size_buf,fin);
                        if (size_read < size_buf)
                          if (feof(fin)==0)
                          {
                              Log::error() << "Could NOT add "<<(*it) << endl;
                              err = ZIP_ERRNO;
                          }

                        if (size_read>0)
                        {
                          err = zipWriteInFileInZip(zf,buf,size_read);
                          if (err<0)
                          {
                              Log::error() << "Could NOT write KMZ file "<< fileName_<< endl;
                          }
                        }
                       } while ((err==ZIP_OK) && (size_read>0));
                    }
                    if (fin)
                        fclose(fin);
                  }

                  if (err<0)
                        err=ZIP_ERRNO;
                  else
                  {
                        err = zipCloseFileInZip(zf);
                        if (err!=ZIP_OK)
                              printf("error in closing xxxx in the zipfile\n");
                  }
//                      delete [] filename;
            }
            free(buf);

            err = zipClose(zf,0);
            if (err != ZIP_OK)
                  printf("error in closing %s\n",fileName_.c_str());
            else if (!getDebug())
            {
                  stringarray::iterator it = kml_output_resource_list_.begin();
                  stringarray::iterator itend = kml_output_resource_list_.end();
                  for(; it != itend; it++)
                  {
                        remove((*it).c_str());
                  }
                  printOutputName("KML kmz "+fileName_);
            }
         }// Zipping ---> K M Z
      }
      else if(!kmz_)
      {
            stringarray::iterator it = kml_output_resource_list_.begin();
            stringarray::iterator itend = kml_output_resource_list_.end();
            for(; it != itend; it++)
            {
                  printOutputName("KML misc "+(*it));
            }
      }
}

/*!
  \brief starting a new page

  This method has to take care that previous pages are closed and that
  for formats with multiple output files a new file is set up.

  \note There is NO page concept in KML!
*/
00275 MAGICS_NO_EXPORT void KMLDriver::startPage() const
{
      currentPage_++;

      debugOutput("Start Page");

      polyline_begin_ = true;
      polygon_begin_  = true;
      currentLayer_   = "none";
}

/*!
  \brief ending a page

  This method has to take care that for formats with multiple output
  files are closed.

  \note There is NO page concept in KML!
*/
00294 MAGICS_NO_EXPORT void KMLDriver::endPage() const
{
      if (kml_placemark_) closePlacemark();

      layer_=false;
      debugOutput("Close page");
}

/*!
  \brief project to a new Layout

  This method will update the offset and scale according to the new Layout given.

  \sa Layout
*/
00309 MAGICS_NO_EXPORT void KMLDriver::project(const magics::Layout& layout) const
{
Log::dev() << " KMLDriver::project("<<layout.id()<<")"<< endl;
}

/*!
  \brief setup a new layer

  This method will setup a new layer. Layers enable overlays of entities
  of information.

  \sa Layer
*/
00322 MAGICS_NO_EXPORT void KMLDriver::redisplay(const Layer& layer) const
{
  if(!magCompare("coastlines",layer.name()) || getCoastlines() )
  {
//    if(!magCompare(currentLayer_,layer.name()))
//    {
            currentLayer_     = layer.name();
            currentTimeBegin_ = layer.kmlTimeBegin();
            currentTimeEnd_   = layer.kmlTimeEnd();
cout << "layer "<<layer.name()<<" "<<layer.kmlTimeBegin()<< endl;       
            newLayer();
            layer.visit(*this);
            closeLayer();
//    }
//    else
//          layer.visit(*this);
  }
}

/*!
  \brief open new layer

*/
00345 MAGICS_NO_EXPORT void KMLDriver::newLayer() const
{
      if (kml_placemark_) closePlacemark();

      debugOutput("Start Layer - "+currentLayer_);

      pFile_ << "<Folder>\n<name>"<<currentLayer_<<"</name>\n<open>0</open>\n"
             << " <styleUrl>#check-hide-children</styleUrl>\n";

      polyline_begin_=true;
      polygon_begin_=true;
      layer_=true;
      render_=true;
}


/*!
  \brief close the current layer

  This method will close an existing layer. This includes resets of existing boxes.

*/
00367 MAGICS_NO_EXPORT void KMLDriver::closeLayer() const
{
      if (kml_placemark_) closePlacemark();

      layer_=false;
      render_=false;
      pFile_      << "</Folder>\n";
      debugOutput("Close Layer - "+currentLayer_);
}


MAGICS_NO_EXPORT void KMLDriver::closePlacemark() const
{
      if(MultiGeometrySet_)
      {
            pFile_ << "</MultiGeometry>\n";
            MultiGeometrySet_=false;
      }
      pFile_ << "</Placemark>\n";
      kml_placemark_=false;
//    currentLayer_ = "non";

      if(!polygon_begin_) polygon_begin_=true;
}

/*!
  \brief sets a new colour

  This colour stays the default drawing colour until the painting in the
  current box is finished.

  Expression is aabbggrr, where aa=alpha (00 to ff); bb=blue (00 to ff);
  gg=green (00 to ff); rr=red (00 to ff)

  \sa Colour
*/
00403 MAGICS_NO_EXPORT void KMLDriver::setNewColour(const Colour &colour) const
{
      currentColour_ = colour;
}

/*!
  \brief sets a new line width

  This line width stays the default width until the painting in the
  current box is finished.

  \sa setLineParameters()
*/
00416 MAGICS_NO_EXPORT void KMLDriver::setNewLineWidth(const float width) const
{
      currentLineWidth_ = (width<1) ? currentLineWidth_=1. : width;
}

/*!
  \brief sets new properties of how lines are drawn

  These properties stay the default until the painting in the
  current box is finished.

  \sa LineStyle

  param linestyle Object describing the line style
  param w width of the line

*/
00433 MAGICS_NO_EXPORT int KMLDriver::setLineParameters(const LineStyle , const float w) const
{
      setNewLineWidth(w);

      Log::debug() << "KMLDriver::setLineParameters needs implementing." <<endl;
      return 0;
}

/*!
  \brief renders polylines

  This method renders a polyline given as two float arrays. The two
  arrays given as X and Y values have to be at least the length of
  <i>n</i>. All values beyond <i>n</i> will be ignored. The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00454 MAGICS_NO_EXPORT void KMLDriver::renderPolyline(const int n, float *x, float *y) const
{
  if(render_ && n > 1 )
  {
      if(!kml_placemark_ || polyline_begin_)
      {
            if (kml_placemark_) closePlacemark();
            pFile_ << "<Placemark>\n";
            if(layer_)
            {
                  pFile_ << "<name>"<<currentLayer_<<"</name>\n";
                  if(!currentTimeBegin_.empty())
                  {
                     if(magCompare(currentTimeBegin_,currentTimeEnd_))
                     {
                        pFile_      << "<TimeStamp>\n"
                              << " <when>"<<currentTimeBegin_<<"</when>\n"
                              << "</TimeStamp>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                     else
                     {
                        pFile_      << "<TimeSpan>\n"
                              << " <begin>"<<currentTimeBegin_<<"</begin>\n"
                              << " <end>"<<currentTimeEnd_<<"</end>\n"
                              << "</TimeSpan>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                  }
                  pFile_      << "<description><![CDATA["<<currentLayer_<<"]]></description>\n";
            }
            pFile_ << "<visibility>1</visibility>\n<open>0</open>\n";

            kml_placemark_=true;

            const int r = (int)currentColour_.red()*255;
            const int g = (int)currentColour_.green()*255;
            const int b = (int)currentColour_.blue()*255;
            const int a = (int)(getTransparency() * 2.55);

            pFile_      << "<Style>\n<LineStyle>\n"
                  << "\t<color>" <<hex;
                  if(a>15)    pFile_ <<a;
                  else        pFile_ <<"0"<< a;
                  if(b>15)    pFile_ <<b;
                  else        pFile_ <<"0"<< b;
                  if(g>15)    pFile_ <<g;
                  else        pFile_ <<"0"<< g;
                  if(r>15)    pFile_ <<r;
                  else        pFile_ <<"0"<< r;
            pFile_      << "</color>\n" << dec
                  << "\t<width>"<<currentLineWidth_<<"</width>\n"<<"</LineStyle>\n"
                  << "</Style>\n"
                  << "<MultiGeometry>\n";
            MultiGeometrySet_=true;
            polyline_begin_=false;
      }

      pFile_      << "<LineString>\n"
            << " <extrude>0</extrude>\n"
            << " <altitudeMode>relativeToGround</altitudeMode>\n"
            << " <tessellate>1</tessellate>\n"
            << " <coordinates>\n";

      for(int is=0;is<n;is++)
      {
            pFile_ <<"\t"<< x[is]<<","<<y[is]<<","<<getHeight()*1000<<"\n";
      }
      pFile_ << " </coordinates>\n</LineString>\n";
  }
}


/*!
  \brief renders a single line

  This method renders a polyline with two points.The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00538 MAGICS_NO_EXPORT void KMLDriver::renderPolyline2(const int , float* , float* ) const
{
      Log::debug() << "KMLDriver::renderPolyline2 needs implementing." <<endl;
}

/*!
  \brief renders a filled polygon

  This method renders a filled polygon. The style is
  determined by what is described in the current LineStyle.

  \sa setLineParameters()
  \param n number of points
  \param x array of x values
  \param y array of y values
*/
00554 MAGICS_NO_EXPORT void KMLDriver::renderSimplePolygon(const int n, float* xx, float* yy) const
{
      if (!render_) return;
      if (kml_placemark_) closePlacemark();
      pFile_      << "<Placemark>\n";
      if(layer_)
      {
            pFile_ << "<name>"<<currentLayer_<<"</name>\n"
                   << "<description><![CDATA["<<currentLayer_<<"]]></description>\n";
      }
//        << "<altitudeMode>clampToGround</altitudeMode>\n"
      pFile_<< "<visibility>1</visibility>\n<open>0</open>\n";

//    kml_placemark_=true;

      const int r = (int)(currentColour_.red()*255.);
      const int g = (int)(currentColour_.green()*255.);
      const int b = (int)(currentColour_.blue()*255.);
      const int a = (int)(getTransparency() * 2.55);

      pFile_      << "<Style>\n<PolyStyle>\n"
            << "\t<color>" <<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a;
            if(b>15)    pFile_ <<b;
            else        pFile_ <<"0"<< b;
            if(g>15)    pFile_ <<g;
            else        pFile_ <<"0"<< g;
            if(r>15)    pFile_ <<r;
            else        pFile_ <<"0"<< r;
      pFile_      << "</color>\n" << dec
            << "\t<fill>1</fill>\n</PolyStyle>\n"
            << "<LineStyle>\n"
            << "\t<width>"<<currentLineWidth_<<"</width>\n"
            << "\t<color>" <<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a;
            if(b>15)    pFile_ <<b;
            else        pFile_ <<"0"<< b;
            if(g>15)    pFile_ <<g;
            else        pFile_ <<"0"<< g;
            if(r>15)    pFile_ <<r;
            else        pFile_ <<"0"<< r;
      pFile_      << "</color>\n" << dec
            << "</LineStyle>\n"
            << "</Style>\n"
            << "<MultiGeometry>\n";
      MultiGeometrySet_=true;
//          polygon_begin_=false;

      pFile_ << "<Polygon>\n"
             << " <extrude>1</extrude>\n"
      //       << " <altitudeMode>relativeToGround</altitudeMode>\n"
             << " <altitudeMode>clampToGround</altitudeMode>\n"
             << " <tessellate>1</tessellate>\n"
             << "  <outerBoundaryIs>\n"
             << "   <LinearRing>\n"
             << "    <coordinates>\n";

      for(int it=0;it<n;it++)
      {
            pFile_ <<"\t"<< xx[it] <<","<< yy[it] <<","<<getHeight()*1000<<"\n";
      }

      pFile_ << "    </coordinates>\n"
             << "   </LinearRing>\n"
             << "  </outerBoundaryIs>\n";

      pFile_ << "</Polygon>\n";
      pFile_ << "</MultiGeometry>\n</Placemark>";
}

/*!
  \brief renders text strings

  This method renders given text strings.

  \note As of version 2.0 there are two forms of describing text in Text.

  \sa Text
  \param text object containing the strings and their description
*/
00636 MAGICS_NO_EXPORT void KMLDriver::renderText(const Text& text) const
{
      if(text.empty()) return;
}

/*!
  \brief drawing a circle

  This method renders given text strings.

  The meaning of the last parameter <i>s</i> is as follows:
     - 0-8 determines how many quarters of the circle are filled. Starting from the top clock-wise.
     - 9 fills the whole circle but leaves a vertical bar empty in the middle of the circle.

  \param x X Position
  \param y Y Position
  \param r Radius of circle
  \param s Style which determines how the circle is shaded
*/
00655 MAGICS_NO_EXPORT void KMLDriver::circle(const float , const float , const float , const int ) const
{
      Log::debug() << "KMLDriver::circle needs implementing." <<endl;
}

/*!
  \brief render pixmaps

  This method renders pixmaps. These are used for cell shading and raster input (GIFs and PNGs).

  \sa renderCellArray()

  param x0 x of lower corner
  param y0 y of lower corner
  param x1 x of higher corner
  param y1 y of higher corner
  param w width of pixmap
  param h height of pixmap
  param pixmap contents

*/
00676 MAGICS_NO_EXPORT bool KMLDriver::renderPixmap(float x0,float y0,float x1,float y1,
                                            int w,int h,unsigned char* pixmap,int, bool) const
{
  if(render_)
  {
      if (kml_placemark_) closePlacemark();
#ifndef MAGICS_CAIRO
#ifndef MAGICS_RASTER
      Log::warning() << "Image import is not implemented for the used driver!!!" << endl; return false;
#else
Log::dev()<< "KML driver Image import uses GD -> for better results use Cairo backend."<< endl;
      stringstream out;
      out << 15 * kml_output_resource_list_.size();
      const string filename = "KML_overlay_"+out.str()+"png";

      pFile_      << "<GroundOverlay>\n";
      if(layer_)
      {
            if(!currentTimeBegin_.empty())
            {
                     if(magCompare(currentTimeBegin_,currentTimeEnd_))
                     {
                        pFile_      << "<TimeStamp>\n"
                              << " <when>"<<currentTimeBegin_<<"</when>\n"
                              << "</TimeStamp>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                     else
                     {
                        pFile_      << "<TimeSpan>\n"
                              << " <begin>"<<currentTimeBegin_<<"</begin>\n"
                              << " <end>"<<currentTimeEnd_<<"</end>\n"
                              << "</TimeSpan>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
            }
            pFile_      << "<name><![CDATA[Layer: "<<currentLayer_<<"]]></name>\n";
      }

      const int a = (int)(getTransparency() * 2.55);
      pFile_ << "<styleUrl>#hiker-icon</styleUrl>\n"
            << "<color>"<<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a << dec;
      pFile_ << "ffffff</color>\n"
            << "<visibility>1</visibility>\n"
            << "<color>"<<hex;
                  if(a>15)    pFile_ <<a;
                  else        pFile_ <<"0"<< a << dec;
      pFile_ << "FFFFFF</color>\n"
            << "<Icon>\n"
            << "<href>"<<filename<<"</href>\n"
//          << "<refreshMode>onInterval</refreshMode>\n"
//          << "<refreshInterval>86400</refreshInterval>\n"
//          << "<viewBoundScale>0.75</viewBoundScale>\n"
            << "</Icon>\n"
            << "<LatLonBox>\n"
            << "   <north>"<<y0<<"</north>\n"
            << "   <south>"<<y1<<"</south>\n"
            << "   <east>"<<x1<<"</east>\n"
            << "   <west>"<<x0<<"</west>\n"
            << "   <rotation>0</rotation>\n"
            << "</LatLonBox>\n";


      gdImagePtr im = gdImageCreateTrueColor(w,h);
      unsigned char *p = pixmap;
      gdImageColorAllocateAlpha(im, 255, 255, 255, 127);

      for(int i=h-1;i>=0;i--)
      {
            for(int j=0;j<w; j++)
            {
                  const int r = (int) *(p++);
                  const int g = (int) *(p++);
                  const int b = (int) *(p++);
                  const int col = gdImageColorResolveAlpha(im,r,g,b,0);
                  gdImageSetPixel(im, w, h, col);
            }
      }
      gdImageDestroy(im);
      gdImageAlphaBlending(im, 1);
      gdImageSaveAlpha(im, 1); // save transparency

      FILE *outFile = fopen(filename.c_str(),"wb");
      gdImagePng(im,outFile);
      fclose(outFile);
      kml_output_resource_list_.push_back(filename);

      pFile_      << "</GroundOverlay>\n";
#endif
#else
      stringstream out;
      out << 15 * kml_output_resource_list_.size();
      string filename = "KML_overlay_"+out.str()+"png";

      pFile_      << "<GroundOverlay>\n";
      if(layer_)
      {
            if(!currentTimeBegin_.empty())
            {
                     if(magCompare(currentTimeBegin_,currentTimeEnd_))
                     {
                        pFile_      << "<TimeStamp>\n"
                              << " <when>"<<currentTimeBegin_<<"</when>\n"
                              << "</TimeStamp>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                     else
                     {
                        pFile_      << "<TimeSpan>\n"
                              << " <begin>"<<currentTimeBegin_<<"</begin>\n"
                              << " <end>"<<currentTimeEnd_<<"</end>\n"
                              << "</TimeSpan>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
            }
            pFile_      << "<name><![CDATA[Time: "<<currentLayer_<<"]]></name>\n";
      }

      const int a = (int)(getTransparency() * 2.55);
      pFile_      << "<styleUrl>#hiker-icon</styleUrl>\n"
            << "<color>"<<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a << dec;
      pFile_ << "ffffff</color>\n"
            << "<visibility>1</visibility>\n"
            << "<color>"<<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a << dec;
      pFile_ << "FFFFFF</color>\n"
            << "<Icon>\n"
            << "<href>"<<filename<<"</href>\n"
//          << "<refreshMode>onInterval</refreshMode>\n"
//          << "<refreshInterval>86400</refreshInterval>\n"
//          << "<viewBoundScale>0.75</viewBoundScale>\n"
            << "</Icon>\n"
            << "<LatLonBox>\n"
            << "   <north>"<<y0<<"</north>\n"
            << "   <south>"<<y1<<"</south>\n"
            << "   <east>"<<x1<<"</east>\n"
            << "   <west>"<<x0<<"</west>\n"
            << "   <rotation>0</rotation>\n"
            << "</LatLonBox>\n";

      cairo_surface_t* surface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,w,h);
      cairo_t* cr_ = cairo_create(surface_);

// \todo specify layer transparency

      // set background to transparent
//    cairo_save (cr_);
      cairo_set_source_rgba (cr_, 1.0, 1.0, 1.0, 0.0);
//    cairo_set_operator (cr_, CAIRO_OPERATOR_SOURCE);
      cairo_paint (cr_);
//    cairo_restore (cr_);

      unsigned char *p = pixmap;
      const float dx =  (x1 - x0)/w;
      const float dy = -(y1 - y0)/h;   // Minus needed for Y axis correction

      const float X0 = x0;
      const float Y0 = y0;

      for(int i=h-1;i>=0;i--)
      {
            for(int j=0;j<w; x0+=dx,j++)
            {
                  const float r = *(p++);
                  const float g = *(p++);
                  const float b = *(p++);

                  if( (r*g*b) > 0)
                  {
                        cairo_set_source_rgba(cr_,r,g,b,0.5);

                        const float x0 = X0+(j*dx)+.5;
                        const float y0 = Y0+(i*dy)+.5;
                        cairo_rectangle (cr_, x0,y0,dx,-dy);
                        cairo_stroke_preserve(cr_);
                        cairo_fill (cr_);
                  }
            }
            x0 = X0;
            y0 += dy;
      }
      cairo_surface_write_to_png (surface_, filename.c_str());
      cairo_destroy (cr_);
      cairo_surface_destroy (surface_);
      kml_output_resource_list_.push_back(filename);
      pFile_      << "</GroundOverlay>\n";
#endif
   }
      return true;
}

/*!
  \brief render cell arrays

  This method renders cell arrays, also called images in Magics language. These are
  mainly used for satellite data.

  sa renderPixmap()

  param image Object containing an image
*/
00882 MAGICS_NO_EXPORT bool KMLDriver::renderCellArray(const Image& image) const
{
   if(render_)
   {
      if (kml_placemark_) closePlacemark();
#ifndef MAGICS_CAIRO
#ifndef MAGICS_RASTER
      Log::warning() << "Image import is not implemented for the used driver!!!" << endl; return false;
#else
Log::dev()<< "KML driver uses GD -> for better results use Cairo backend."<< endl;
      stringstream out;
      out << currentPage_;
      const string layer_name = (!currentLayer_.empty()) ? currentLayer_ : "default_page";
      const string filename = "KML_cell_overlay_"+layer_name+"_"+out.str()+".png";

      pFile_      << "<GroundOverlay>\n";
      if(layer_)
      {
            if(!currentTimeBegin_.empty())
            {
                     if(magCompare(currentTimeBegin_,currentTimeEnd_))
                     {
                        pFile_      << "<TimeStamp>\n"
                              << " <when>"<<currentTimeBegin_<<"</when>\n"
                              << "</TimeStamp>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                     else
                     {
                        pFile_      << "<TimeSpan>\n"
                              << " <begin>"<<currentTimeBegin_<<"</begin>\n"
                              << " <end>"<<currentTimeEnd_<<"</end>\n"
                              << "</TimeSpan>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
            }
            pFile_      << "<name><![CDATA[Layer: "<<currentLayer_<<"]]></name>\n";
      }

      const int a = (int)(getTransparency() * 2.55);
      pFile_      << "<color>"<<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a << dec;
      pFile_ << "ffffff</color>\n"
            << "<visibility>1</visibility>\n"
            << "<Icon>\n"
            << "<href>"<<filename<<"</href>\n"
//          << "<refreshMode>onInterval</refreshMode>\n"
//          << "<refreshInterval>86400</refreshInterval>\n"
//          << "<viewBoundScale>0.75</viewBoundScale>\n"
            << "</Icon>\n"
            << "<LatLonBox>\n"
            << "   <north>"<<image.getOrigin().y()<<"</north>\n"
            << "   <south>"<<image.getOrigin().y()-image.getHeight()<<"</south>\n"
            << "   <east>"<<image.getOrigin().x()+image.getWidth()<<"</east>\n"
            << "   <west>"<<image.getOrigin().x()<<"</west>\n"
            << "   <rotation>0</rotation>\n"
            << "</LatLonBox>\n";

      ColourTable &lt = image.getColourTable();
      const int width = image.getNumberOfColumns();
      const int height = image.getNumberOfRows();

      gdImagePtr im = gdImageCreateTrueColor(width,height);
      gdImageColorAllocateAlpha(im, 255, 255, 255, 127);

      for (int i=height-1;i>=0;i--)
      {
            for(int j=0;j<width; j++)
            {
              const int in = width*i+j;
              const short c = image[in];

              if(!(lt[c]=="undefined"))
              {
                  const int r = static_cast<int>(lt[c].red()*255.);
                  const int g = static_cast<int>(lt[c].green()*255.);
                  const int b = static_cast<int>(lt[c].blue()*255.);
                  const int a = static_cast<int>(lt[c].alpha()*127.);

                  const int col = gdImageColorResolveAlpha(im,r,g,b,a);
                  gdImageSetPixel(im, j, i, col);
              }// point has colour
            }
      }

      gdImageAlphaBlending(im, 1);
      gdImageSaveAlpha(im, 1); // save transparency

      FILE *outFile = fopen(filename.c_str(),"wb");
      gdImagePng(im,outFile);
      fclose(outFile);
      gdImageDestroy(im);
      kml_output_resource_list_.push_back(filename);

      pFile_      << "</GroundOverlay>\n";
#endif
#else
      stringstream out;
      out << currentPage_;
      const string layer_name = (!currentLayer_.empty()) ? currentLayer_ : "default_page";
      string filename = "KML_cell_overlay_"+layer_name+"_"+out.str()+".png";
      string::size_type loc=0;

      while(loc != string::npos)
      {
        loc=filename.find( ":", 0);
          if( loc != string::npos ) filename.replace(loc, 1, "-");
        }

      pFile_      << "<GroundOverlay>\n";
      if(layer_)
      {
            if(!currentTimeBegin_.empty())
            {
                     if(magCompare(currentTimeBegin_,currentTimeEnd_))
                     {
                        pFile_      << "<TimeStamp>\n"
                              << " <when>"<<currentTimeBegin_<<"</when>\n"
                              << "</TimeStamp>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
                     else
                     {
                        pFile_      << "<TimeSpan>\n"
                              << " <begin>"<<currentTimeBegin_<<"</begin>\n"
                              << " <end>"<<currentTimeEnd_<<"</end>\n"
                              << "</TimeSpan>\n"
                              << "<styleUrl>#hiker-icon</styleUrl>\n";
                     }
            }
            pFile_      << "<name><![CDATA[Layer: "<<currentLayer_<<"]]></name>\n";
      }

      const int a = (int)(getTransparency() * 2.55);
      pFile_      << "<color>"<<hex;
            if(a>15)    pFile_ <<a;
            else        pFile_ <<"0"<< a << dec;
      pFile_ << "ffffff</color>\n"
            << "<visibility>1</visibility>\n"
            << "<Icon>\n"
            << "<href>"<<filename<<"</href>\n"
//          << "<refreshMode>onInterval</refreshMode>\n"
//          << "<refreshInterval>86400</refreshInterval>\n"
//          << "<viewBoundScale>0.75</viewBoundScale>\n"
            << "</Icon>\n"
            << "<LatLonBox>\n"
            << "   <north>"<<image.getOrigin().y()<<"</north>\n"
            << "   <south>"<<image.getOrigin().y()-image.getHeight()<<"</south>\n"
            << "   <east>"<<image.getOrigin().x()+image.getWidth()<<"</east>\n"
            << "   <west>"<<image.getOrigin().x()<<"</west>\n"
            << "   <rotation>0</rotation>\n"
            << "</LatLonBox>\n";

      ColourTable &lt = image.getColourTable();
      const int w = image.getNumberOfColumns();
      const int h = image.getNumberOfRows();

      cairo_surface_t* surface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,w,h);
      cairo_t* cr_ = cairo_create(surface_);

// \todo specify layer transparency

      // set background to transparent
//    cairo_save (cr_);
      cairo_set_source_rgba (cr_, 1.0, 1.0, 1.0, 0.0);
//    cairo_set_operator (cr_, CAIRO_OPERATOR_SOURCE);
      cairo_paint (cr_);
//    cairo_restore (cr_);

      const float dx = 1.;
      const float dy = 1.;

      for (int i=h-1;i>=0;i--)
      {
            for(int j=0;j<w; j++)
            {
              const int in = w*i+j;
              const short c = image[in];

              if(!(lt[c]=="undefined"))
              {
                  float r = lt[c].red();
                  float g = lt[c].green();
                  float b = lt[c].blue();
                  float a = lt[c].alpha();

                  if( (r*g*b>1.) || (r*g*b<0.) )
                  {
                        r = 1.;
                        g = 1.;
                        b = 1.;
                        a = 0.;
//                      Log::info()<< "PostScriptDriver-> Cellshading colour not defined in table! Colour index: "<<c<<endl;
//    PostScript will always 'overpaint' anything below missing data!!!!
//
                  }
                  cairo_set_source_rgba(cr_,r,g,b,a);

                  const float x0 = (j*dx);
                  const float y0 = (i*dy);
                  cairo_rectangle (cr_, x0,y0,dx,-dy);
                  cairo_stroke_preserve(cr_);
                  cairo_fill (cr_);
//                gdImageSetPixel(im, j, i, col);
              }// point has colour
            }
      }

      cairo_surface_write_to_png (surface_, filename.c_str());

      cairo_destroy (cr_);
      cairo_surface_destroy (surface_);

      kml_output_resource_list_.push_back(filename);
      pFile_      << "</GroundOverlay>\n";
#endif
   }
      return true;
}


//! Method to plot symbols
/*!
 Needs special treatment of Logo. The logo is added on close().

 \sa close
*/
01110 MAGICS_NO_EXPORT void KMLDriver::renderSymbols(const Symbol& symbol) const
{
      if(symbol.getSymbol()=="logo_ecmwf")
            ecmwf_logo_=true;
//    else
//          BaseDriver::renderSymbols(symbol);
}

/*!
  \brief prints debug output

  When Magics++ is compiled in debug mode these extra strings are printed.

  \note This can increase file and log file sizes if you run Magics++ in debug mode!

  \param s string to be printed
*/
01127 MAGICS_NO_EXPORT void KMLDriver::debugOutput(const string &s) const
{
      if(getDebug()) pFile_ << "<!-- "<<s<<" -->\n";
}

/*!
  \brief class information are given to the output-stream
*/
01135 void KMLDriver::print(ostream& out)  const
{
      out << "KMLDriver[";
      out << "]";
}

static SimpleObjectMaker<KMLDriver, BaseDriver> KML_driver("KML");

Generated by  Doxygen 1.6.0   Back to index