Skip to main content

Tooltopia

Part of recent events I've been busy on a streaming module of terrain rendering allowing an virtually unlimited terrain. While this is trivial there is still a problem of getting content. Luckily there are published elevation files,  SMRT or  it's predecessor DEM, from earth's key locations e.g. Taiwan. To process elevation information we do what we engineers do best, write tools.




I decided to step out of my comfort box and give C++CLI a shot.  Despite what people say mixed-mode assembly capabilities of C++CLI is quite a nice. We get to write all the goodies of .net exception being pretty syntax, and  we get to have low-level memory accessibility. It is truly ideal for some quick visualizations  without the need of going through the standard project setup workflow: downloading boost, downloading Qt, compiling them, add in some other third party libraries, reinvent code.
private: System::Void loadToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e) {
  System::Windows::Forms::OpenFileDialog^ dialog = gcnew System::Windows::Forms::OpenFileDialog();
  dialog->Filter = "SRTM (*.hgt)|*.hgt";
  dialog->Multiselect = true;

  float minAltitude = -62;
  float maxAltitude = 3811;
  float altitudeRange = maxAltitude - minAltitude;

  if( dialog->ShowDialog() == System::Windows::Forms::DialogResult::OK )
  {

  System::Drawing::Color ocean = System::Drawing::Color::FromArgb(255, 60, 80, 100);
  System::Drawing::Bitmap^ gradiant = gcnew System::Drawing::Bitmap(1,1024, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
  if( gradiant != nullptr  )
  {
   System::Drawing::Drawing2D::LinearGradientBrush^ brush = gcnew System::Drawing::Drawing2D::LinearGradientBrush(
    System::Drawing::Rectangle(0,0,1,1024), System::Drawing::Color::Black, System::Drawing::Color::White,
    System::Drawing::Drawing2D::LinearGradientMode::Vertical);

   System::Drawing::Drawing2D::ColorBlend^ blend = gcnew System::Drawing::Drawing2D::ColorBlend();
   blend->Positions = gcnew cli::array<float>(14);
   blend->Colors = gcnew cli::array<System::Drawing::Color>(14);
   blend->Positions[ 0] =(  -62 - minAltitude) / altitudeRange; blend->Colors[ 0] = System::Drawing::Color::FromArgb(255, 139, 146, 112);
   blend->Positions[ 1] =(  284 - minAltitude) / altitudeRange; blend->Colors[ 1] = System::Drawing::Color::FromArgb(255, 158, 159, 117);
   blend->Positions[ 2] =(  578 - minAltitude) / altitudeRange; blend->Colors[ 2] = System::Drawing::Color::FromArgb(255, 177, 173, 123);
   blend->Positions[ 3] =(  872 - minAltitude) / altitudeRange; blend->Colors[ 3] = System::Drawing::Color::FromArgb(255, 196, 186, 129);
   blend->Positions[ 4] =( 1166 - minAltitude) / altitudeRange; blend->Colors[ 4] = System::Drawing::Color::FromArgb(255, 215, 200, 135);
   blend->Positions[ 5] =( 1460 - minAltitude) / altitudeRange; blend->Colors[ 5] = System::Drawing::Color::FromArgb(255, 208, 190, 128);
   blend->Positions[ 6] =( 1754 - minAltitude) / altitudeRange; blend->Colors[ 6] = System::Drawing::Color::FromArgb(255, 202, 180, 121);
   blend->Positions[ 7] =( 2048 - minAltitude) / altitudeRange; blend->Colors[ 7] = System::Drawing::Color::FromArgb(255, 195, 170, 114);
   blend->Positions[ 8] =( 2342 - minAltitude) / altitudeRange; blend->Colors[ 8] = System::Drawing::Color::FromArgb(255, 189, 160, 107);
   blend->Positions[ 9] =( 2636 - minAltitude) / altitudeRange; blend->Colors[ 9] = System::Drawing::Color::FromArgb(255, 183, 150, 101);
   blend->Positions[10] =( 2930 - minAltitude) / altitudeRange; blend->Colors[10] = System::Drawing::Color::FromArgb(255, 179, 154, 113);
   blend->Positions[11] =( 3224 - minAltitude) / altitudeRange; blend->Colors[11] = System::Drawing::Color::FromArgb(255, 175, 158, 126);
   blend->Positions[12] =( 3518 - minAltitude) / altitudeRange; blend->Colors[12] = System::Drawing::Color::FromArgb(255, 171, 162, 138);
   blend->Positions[13] =( 3811 - minAltitude) / altitudeRange; blend->Colors[13] = System::Drawing::Color::FromArgb(255, 167, 167, 151);
   brush->InterpolationColors = blend;

   System::Drawing::Graphics^ g = System::Drawing::Graphics::FromImage(gradiant);
   g->FillRectangle( brush, System::Drawing::Rectangle(0,0,1,1024));
   delete g;
  }

  int minLongitude = 0x7FFFFFFF, maxLongitude = 0xFFFFFFFF;
  int minLatitude = 0x7FFFFFFF, maxLatitude = 0xFFFFFFFF;
     System::Text::RegularExpressions::Regex^ regex = gcnew System::Text::RegularExpressions::Regex("(N|S)(d+)(E|W)(d+).hgt");
  for( int i = 0; i < dialog->FileNames->Length; i++ )
  {
   System::Text::RegularExpressions::Match^ matches = regex->Match( dialog->FileNames[i] );
   System::IO::FileInfo^ info = gcnew System::IO::FileInfo( dialog->FileNames[i] );
   int numberOfEntries = (info->Length / 2), gridSize = System::Math::Sqrt( numberOfEntries );
   int latitude = System::Int32::Parse( matches->Groups[2]->Value );
   int longitude = System::Int32::Parse( matches->Groups[4]->Value );
   minLatitude = System::Math::Min( minLatitude, latitude );
   maxLatitude = System::Math::Max( maxLatitude, latitude + 1 );
   minLongitude = System::Math::Min( minLongitude, longitude );
   maxLongitude = System::Math::Max( maxLongitude, longitude + 1 );
  }

  int numberOfBlocksWidth = maxLongitude - minLongitude;
  int numberOfBlocksHeight = maxLatitude - minLatitude;
  int pixelsWidth = (numberOfBlocksWidth * 1201);
  int pixelsHeight = (numberOfBlocksHeight * 1201);

  cli::array<signed short,2>^ values = gcnew cli::array<signed short,2>(pixelsWidth, pixelsHeight);
  for( int y = 0; y < pixelsHeight; y++ ) for( int x = 0; x < pixelsWidth; x++ )
   values[x,y] = -100;

  System::Drawing::Bitmap^ bitmap = gcnew System::Drawing::Bitmap(pixelsWidth,pixelsHeight, System::Drawing::Imaging::PixelFormat::Format32bppArgb );
  System::Drawing::Graphics^ g = System::Drawing::Graphics::FromImage(bitmap);
  g->Clear( ocean );
  delete g;

  for( int i = 0; i < dialog->FileNames->Length; i++ )
  {
   System::Text::RegularExpressions::Match^ matches = regex->Match( dialog->FileNames[i] );
   System::IO::FileStream^ fs = System::IO::File::OpenRead(dialog->FileNames[i]);
   int latitude = System::Int32::Parse( matches->Groups[2]->Value );
   int longitude = System::Int32::Parse( matches->Groups[4]->Value );
   int length = fs->Length / 2, gsize= System::Math::Sqrt( length );

   int pixelOffsetX = gsize * (longitude - minLongitude), pixelOffsetY = gsize * (latitude - minLatitude);
   //pixelOffsetX = pixelsWidth - gsize - pixelOffsetX;
   pixelOffsetY = pixelsHeight - gsize - pixelOffsetY;

   System::Drawing::Imaging::BitmapData^ data = bitmap->LockBits(  System::Drawing::Rectangle(pixelOffsetX,pixelOffsetY,gsize,gsize), System::Drawing::Imaging::ImageLockMode::WriteOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb );
   for(int y=0; y < gsize; y++)
   {
    System::Byte* row=   (System::Byte*)(data->Scan0.ToPointer() ) + System::Int32(y*data->Stride);
    for(int x=0; x< gsize; x++)
    {     

     signed short value = (fs->ReadByte() << 8) | (fs->ReadByte()  << 0);
     values[pixelOffsetX + x, pixelOffsetY + y] = value;
     signed short valueClamped = System::Math::Max( System::Math::Min( value, (signed short)maxAltitude ), (signed short)minAltitude );
     signed int pixelOffset = (int)((valueClamped - minAltitude) / altitudeRange * 1023.0);
     System::Drawing::Color color = gradiant->GetPixel( 0, pixelOffset);
     if( value <= 0 )  color = ocean;

     row[x * 4 + 0] = color.B;
     row[x * 4 + 1] = color.G;
     row[x * 4 + 2] = color.R;
     row[x * 4 + 3] = 255;
    }
   }

   bitmap->UnlockBits(data );
   fs->Close();
  }       

  pictureBox1->Image = bitmap;
  pictureBox1->Bounds = System::Drawing::Rectangle(0,0, numberOfBlocksWidth * 1201, numberOfBlocksHeight * 1201);
  data_values = values;
  data_size_x = numberOfBlocksWidth * 1201;
  data_size_y = numberOfBlocksHeight * 1201;
  }
 }



Comments

Popular posts from this blog

Getting started with Electron Pt 1.

Electron is a fun and easy way to create desktop application from an mostly web based code. Of course websites aren't the most performance way to create an user-interface ( in terms of technical aspects such as memory, cpu consumption) but it's an extremely powerful experience rich way of doing that.

How to configure GoDaddy domain to blogspot

1. Sign in to your GoDaddy account. Click on "My Account," and then click on "My Products." Click "DNS" beneath the corresponding domain.   2. Click on "Add Record," and then select "CNAME (Alias)." 3. Type the desired subdomain name in the "Alias" box. In the "Points To" box, type the following: ghs.google.com 5. Click on "Save Zone File." 6. Sign in to your Blogspot account. Click on "Settings." Click on "Basic." 7. Go to the section ‘Publish’, click the link ‘add URL from thirdparty’   then type your custom  subdomain in the "Your Domain" box. 8. Fill out the word verification puzzle, and then click on "Save Settings." Choose whether or not to redirect the primary domain to the subdomain.
Did you know that the std::istream model is pretty cool? In a few lines of code you can create your own streambuf implementation that drives the istream model.