9.13.2013

Stitching together geoTiff files with gdal

Recently I was asked to import some geotiffs into a geoserver instance. I also needed to stitch the geotiffs together so I didn't have to create 100 some odd layers in geoserver.

I found a helpful python script here:
https://gist.github.com/philipn/1148693

But it was producing files that were way too big for my geoserver to handle. Inspired, I wrote the following to group a bunch of tiffs together in several composite geotiffs:
 
import os
import shutil
import sys
import time
import subprocess
from collections import OrderedDict


DIRS = ['/mnt/hgfs/f/imagery/va', '/mnt/hgfs/f/imagery/md']
GROUP_SIZE = 8

def runCmd( cmd ):
 #print cmd
 return subprocess.call( cmd, shell=True )

def tif(l) : return l.endswith( '.tif' ) and not l.startswith('group')

def sizeof_fmt(num):
 for x in ['bytes', 'kb', 'mb', 'gb', 'tb']:
  if num < 1024.0:
   return "%3.2f %s" % (num, x)
  num /= 1024.0
 return "%3.2f%s" % (num, ' tb')

def human_time( secs ):
 interval = OrderedDict( [("Y", 365*86400),
                          ("M", 30*86400),
                          ("W", 7*86400),
                          ("D", 86400),
                          ("h", 3600),
                          ("m", 60),
                          ("s", 1)])
 secs = int(secs)
 s = ""
 for unit, value in interval.items():
  subres = secs / value
  if subres > 0:
   secs = secs % subres
   s += str(subres) + unit
 return s



totalFilesProcessed = 0
totalFileCount = sum([len( filter( tif, sorted( os.listdir( dir ) ) ) ) for dir in DIRS])
pStart = time.time()
secsPerFile = 0;
for dir in DIRS:
 dStart = time.time()
 files = sorted( os.listdir( dir ) );
 files = filter( tif, files );
 i = 0
 rel_i = i * GROUP_SIZE
 dirFilesProcessed = 0
 dirFileCount = len(files)
 while( rel_i < dirFileCount ):
  gStart = time.time()
  tifsToProcess = [os.path.join(dir,x) for x in files[rel_i:rel_i + GROUP_SIZE]]
  finalTif = os.path.join(dir, 'group-%i.tif' % i)

  if os.path.exists( finalTif ):
   print 'WARNING, %s exists, removing...' % finalTif
   os.remove( finalTif )

  print 'processing %i files in group %i inside %s:\n\t%s\n' % (len(tifsToProcess), i, dir, '\n\t'.join(tifsToProcess))
 
  print 'uncompressing and nearblack\'ing...'
  for t in tifsToProcess:
   uc = '%s.uncompressed' % t
   if not os.path.exists( uc ):
    cmd = '/usr/local/bin/gdal_translate %s %s' % (t, uc)
    if runCmd( cmd ) != 0:
     sys.exit(1)
    cmd = '/usr/local/bin/nearblack -near 20 %s' %uc 
    if runCmd( cmd ) != 0:
     sys.exit(1)

  print '\nstitching...'
  cmd = '/usr/local/bin/gdalwarp -dstalpha -srcnodata 0 -wo "SKIP_NOSOURCE" --config "GDAL_CACHEMAX=500" -wm=500 %s.uncompressed %s' % ('.uncompressed '.join(tifsToProcess), finalTif) 
  if runCmd( cmd ) != 0:
   sys.exit(1)

  print '\nremoving temporary files...'
  for t in tifsToProcess:
   #print 'removing: %s.uncompressed' % t
   os.remove( '%s.uncompressed' % t )

  print '\nadding overviews...'
  cmd = '/usr/local/bin/gdaladdo %s 2 4 8 16 32' % finalTif
  if runCmd( cmd ) != 0:
   sys.exit(1)

  gStop = time.time()
  dirFilesProcessed += len(tifsToProcess)
  totalFilesProcessed += len(tifsToProcess)

  print '\nproduced %s file in %s: %s' % (sizeof_fmt(os.path.getsize(finalTif)), human_time(gStop - gStart), finalTif)
  print '%3.1f%% complete with directory %s. %i / %i files remain' % ((dirFilesProcessed * 100) / float(dirFileCount), dir, dirFileCount - dirFilesProcessed, dirFileCount )
  print '%3.1f%% complete with total. %i / %i files remain' % ((totalFilesProcessed * 100) / float(totalFileCount), totalFileCount - totalFilesProcessed, totalFileCount)

  secsPerFile = int((gStop - pStart) / (totalFilesProcessed))
  print 'estimated time remaining for directory: %s' % (human_time( (dirFileCount - dirFilesProcessed) * secsPerFile ))
  print 'estimated time remaining for all dirs:  %s' % (human_time( (totalFileCount - totalFilesProcessed) * secsPerFile))
  print '\n\n'

  i += 1
  rel_i = i * GROUP_SIZE

 dStop = time.time()
 print 'processed dir %s in %s' % (dir, human_time(dStop - dStart))

pStop = time.time()
print 'total runtime: %s' % (human_time(pStop - pStart))

Basically this script uncompresses and runs nearblack on each tiff in a group and then stitches them together using gdalwarp into a single image.  You can define a set of directories to operate over and it will look through and create grouped tiffs in each.

KNOWN ISSUES

One issue with this script is it will blindly take the first 8 (in its posted state) files and stitch them together.  That may be what you want.  But if your tiff filenames are such that the neighbor images may not be next to each other in a directory listing, the script has the potential to generate some really big resulting tiffs.

For example, say you define a GROUP_SIZE of 2 and you have two geoTiffs tile1.tiff and tile2.tiff.  If those two imagery tiles are separated by 100 miles, the resulting tiff will include those two tiles plus whitespace in between them.  The resulting tiff file size is going to be much larger than the sum of the file sizes of tile1 and tile2.

Even one outlier tile has the potential to really drive up filesize if it is far enough away from the rest of the group.

It would be better to somehow parse the geographic information in the group and only stitch together adjacent tiles.  Perhaps you define a max limit to the number of adjacent tiles in a single group.  But it would be nice to avoid generating groups with outliers.

3.01.2013

adding an icon to a C++ project with Visual Studio Express 2012

I wrote a simple application to perform a left mouse click after a certain amount of time elapsed.  Its pretty simple.  Took me maybe a half hour to code it up while wading though MSDN.

It took me at least 3 times as long to add an icon to my project.  Here's how I did it.

First, I created a .png icon.  This is no good, we need an .ico.  Fortunately, there is an awesome website that converts images to .icos:

http://www.icoconverter.com/

I had the best luck with All Sizes and 256 colors.

Next I needed to manually create a resource script for my project.  Real (non-free) versions of Visual Studio do this for you automatically.  Simply use your favorite text editor (mine is gvim) and create a text file that looks something like the following:

programIcon ICON "icon.ico"

I named my script "resources.script" and placed it in the same directory as my icon.ico file.

Next you need to manually run rc.exe.  This should get installed with Visual Studio, mine was located here:

C:\Program Files (x86)\Windows Kits\8.0\bin\x86

I added this location to my path and then opened a shell to the location of my resources.script and ran:

rc.exe resources.script

This produced a "resources.res" file.  I imported this into my Visual Studio project by right clicking the "resources" folder under my project under my solution and "Add Existing".  Then I rebuilt my solution and voila!  icon!


2.06.2013

mp4 video in chrome

I was having a really hard time getting mp4 video to render in Chrome with the html 5 <video> tag.  After screwing around with ffmpeg for a day I think I found the correct settings.  I used the following command line to transcode a wmv to the proper mp4:

$ ffmpeg -i movie.wmv -pix_fmt yuv420p -vf setsar='1/1' vid.mp4

The key difference from the default .wmv -> .mp4 conversion is specifying the pixel format as yuv420p and setting the storage aspect ratio (SAR) to 1:1 (or 1/1). The default using a rgb pixel format and inherites the storage aspect ratio from the source... which may or may not be one to one. The resulting video looks like this:



$ ffprobe -show_format vid.mp4 
ffprobe version 1.1.git Copyright (c) 2007-2013 the FFmpeg developers
  built on Feb  5 2013 07:56:01 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
  configuration: --enable-gpl --enable-libass --enable-libfaac --enable-libfdk-aac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-librtmp --enable-libtheora --enable-libvorbis --enable-x11grab --enable-libx264 --enable-nonfree --enable-version3
  libavutil      52. 17.101 / 52. 17.101
  libavcodec     54. 91.100 / 54. 91.100
  libavformat    54. 61.104 / 54. 61.104
  libavdevice    54.  3.103 / 54.  3.103
  libavfilter     3. 35.101 /  3. 35.101
  libswscale      2.  2.100 /  2.  2.100
  libswresample   0. 17.102 /  0. 17.102
  libpostproc    52.  2.100 / 52.  2.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'vid.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf54.61.104
  Duration: 00:00:17.07, start: 0.000000, bitrate: 1817 kb/s
    Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 880x646 [SAR 1:1 DAR 440:323], 1814 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc
    Metadata:
      handler_name    : VideoHandler
[FORMAT]
filename=vid.mp4
nb_streams=1
format_name=mov,mp4,m4a,3gp,3g2,mj2
format_long_name=QuickTime / MOV
start_time=0.000000
duration=17.067000
size=3877456
bit_rate=1817522
TAG:major_brand=isom
TAG:minor_version=512
TAG:compatible_brands=isomiso2avc1mp41
TAG:encoder=Lavf54.61.104
[/FORMAT]

1.21.2013

Chrome's embedded flash broken by Microsoft's Enhanced Mitigation Experience Toolkit (EMET)

Microsoft's Enhanced Mitigation Experience Toolkit breaks recent versions of Chrome. I had a nagging issue with Chrome in that it wouldn't load the newly sandboxed flash player. This would cause minor irritations like not being able to view youtube videos in Chrome as well as major problems like Google Calendar completely crashing.

After spending a day and a half troubleshooting drivers, services, and startup programs between safe mode and normal mode (Chrome worked fine in safe mode with networking), I uninstalled EMET on a whim and low and behold, Chrome works.

EMET is designed to automatically employ ASLR and structured exception handling protections on executables that weren't necessarily compiled with these features in mind. It also breaks stuff. Lots of stuff by the looks of it. Including Chrome.

I'm not sure which mitigation was breaking Chrome, but uninstalling EMET took care of it :) I wish I knew more about the flash sandboxing implementation in Chrome in order to identify exactly what went wrong. Reversing Chrome's sandboxing sounds like a project for another day. I'm sure PinkiePie knows exactly what happened.

EDIT: Looks like SEHOP is the offending mitigation. Disabling this for chrome.exe may be less hammer like than uninstalling EMET.

11.27.2012

git differencing

i can never remember these, and unfortunately i don't use git often enough to adequately drill it into my head.

To get a name and status difference of what changed between two branches, use this:

git diff --name-status master..branch

To compare two files from different branches, use this:

git difftool branch1:file branch2:file

11.16.2012

Windows 7 64 laptop not entering sleep


I was having trouble with my laptop not going to sleep after the time setting in power options.  After some search, I read that running a power usage report might help identify the problem.

Running the following command from an Administrator shell:

powercfg -energy -output %USERPROFILE%\Desktop\Energy_Report.html

pointed out my audio card was making system requests such that sleep would never trigger.  I updated the drivers on my card and the audio card is no longer holding up the sleep timer.

11.13.2012

symbolic links on NTFS

I had no idea NTFS supported symbolic links.  I know there are shortcuts, but these aren't the same as full blown symlinks.  The reason I thought this is because anytime I tried to ln -s in my Ubuntu VM on a directory that was shared with windows, I'd always get:

ln: failed to create symbolic link './link': Operation not supported.

I was expressing my frustrations to a co-worker who mentioned the mklink command in windows.  Sure enough, minimizing my VM and launching CMD I was able to use mklink to create a symlink in the directory I wanted.  The VM sees it as a regular directory but thats ok for my purposes.  The syntax is a bit different than the posix command ln:

mklink /D <link_name> <link_target>

for directories or

mklink <link_name> <link_target> 

for files.