[osgearth] 04/15: New upstream version 2.9~rc1+dfsg

Bas Couwenberg sebastic at debian.org
Sun Jan 7 14:40:58 UTC 2018


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch experimental
in repository osgearth.

commit 9901b6e2eaa8fe48b3550d6637a5739ceff53dd3
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sun Jan 7 10:13:47 2018 +0100

    New upstream version 2.9~rc1+dfsg
---
 .travis.yml                                        |    12 +-
 CMakeLists.txt                                     |   276 +-
 CMakeModules/FindExpat.cmake                       |    52 -
 CMakeModules/FindGDAL.cmake                        |    18 +-
 CMakeModules/FindGEOS.cmake                        |    15 +-
 CMakeModules/FindGLCORE.cmake                      |    36 +
 CMakeModules/FindJavaScriptCore.cmake              |    46 -
 CMakeModules/FindLevelDB.cmake                     |     5 +
 CMakeModules/FindLibNoise.cmake                    |    64 -
 CMakeModules/FindOSG.cmake                         |    14 +-
 CMakeModules/FindOpenGLES.cmake                    |    12 +
 CMakeModules/FindRocksDB.cmake                     |     2 +
 CMakeModules/FindV8.cmake                          |   245 -
 CMakeModules/ModuleInstall.cmake                   |    12 +-
 CMakeModules/OsgEarthMacroUtils.cmake              |   140 +-
 data/resources/textures_us/catalog.xml             |     4 +
 docs/source/about.rst                              |    41 +-
 docs/source/data.rst                               |    81 +-
 docs/source/developer/shader_composition.rst       |     4 +-
 docs/source/faq.rst                                |     8 +-
 docs/source/index.rst                              |     1 +
 docs/source/references/drivers/tile/tms.rst        |     6 +-
 docs/source/references/earthfile.rst               |    18 +-
 docs/source/references/envvars.rst                 |     2 +-
 docs/source/startup.rst                            |     2 +-
 docs/source/support.rst                            |    24 +
 docs/source/user/features.rst                      |     6 +-
 src/CMakeLists.txt                                 |    22 +-
 src/applications/CMakeLists.txt                    |    38 +-
 src/applications/osgearth_3pv/osgearth_3pv.cpp     |     4 +-
 .../osgearth_annotation/osgearth_annotation.cpp    |    37 +-
 src/applications/osgearth_atlas/osgearth_atlas.cpp |     6 +-
 .../osgearth_boundarygen/BoundaryUtil.cpp          |     2 +-
 .../osgearth_boundarygen/boundarygen.cpp           |     8 +-
 src/applications/osgearth_city/osgearth_city.cpp   |    94 +-
 src/applications/osgearth_clamp/osgearth_clamp.cpp |   178 -
 .../osgearth_clipplane/osgearth_clipplane.cpp      |     2 +-
 .../osgearth_colorfilter/osgearth_colorfilter.cpp  |    11 +-
 .../osgearth_computerangecallback.cpp              |     1 +
 .../osgearth_controls/osgearth_controls.cpp        |     2 +-
 src/applications/osgearth_conv/osgearth_conv.cpp   |   200 +-
 .../osgearth_createtile/osgearth_createtile.cpp    |     8 +-
 .../osgearth_datetime/osgearth_datetime.cpp        |     4 +-
 .../osgearth_deformation/osgearth_deformation.cpp  |    34 +-
 .../osgearth_elevation/osgearth_elevation.cpp      |    85 +-
 .../osgearth_ephemeris/osgearth_ephemeris.cpp      |     7 +-
 .../osgearth_featureeditor.cpp                     |   286 -
 .../osgearth_featurefilter.cpp                     |     3 +-
 .../osgearth_featureinfo/osgearth_featureinfo.cpp  |     6 +-
 .../osgearth_featuremanip.cpp                      |   168 -
 .../osgearth_featurequery.cpp                      |    21 +-
 .../osgearth_features/osgearth_features.cpp        |    63 +-
 src/applications/osgearth_fog/osgearth_fog.cpp     |   100 -
 .../osgearth_graticule/osgearth_graticule.cpp      |   109 +-
 .../osgearth_horizon/osgearth_horizon.cpp          |     5 +-
 .../{osgearth_fog => osgearth_htm}/CMakeLists.txt  |     4 +-
 .../osgearth_htm.cpp}                              |    95 +-
 .../osgearth_imageoverlay.cpp                      |    23 +-
 .../CMakeLists.txt                                 |     5 +-
 .../osgearth_infinitescroll.cpp                    |   207 +
 .../CMakeLists.txt                                 |     4 +-
 .../osgearth_lights/osgearth_lights.cpp            |   244 +
 src/applications/osgearth_los/osgearth_los.cpp     |    63 +-
 src/applications/osgearth_manip/osgearth_manip.cpp |    53 +-
 src/applications/osgearth_map/osgearth_map.cpp     |    43 +-
 .../osgearth_minimap/osgearth_minimap.cpp          |     6 +-
 src/applications/osgearth_mrt/osgearth_mrt.cpp     |    51 +-
 .../CMakeLists.txt                                 |     5 +-
 .../osgearth_noisegen.cpp}                         |    60 +-
 .../osgearth_occlusionculling.cpp                  |     4 +-
 .../osgearth_package/osgearth_package.cpp          |    42 +-
 .../osgearth_package_qt/CMakeLists.txt             |     8 +-
 .../osgearth_package_qt/ExportDialog.cpp           |    45 +-
 .../osgearth_package_qt/PackageQtMainWindow        |    14 +-
 .../osgearth_package_qt/TMSExporter.cpp            |    25 +-
 .../osgearth_pagingtest/osgearth_pagingtest.cpp    |     6 +-
 src/applications/osgearth_pick/osgearth_pick.cpp   |   228 +-
 src/applications/osgearth_qt_simple/CMakeLists.txt |     8 +-
 .../osgearth_qt_windows/CMakeLists.txt             |     8 +-
 .../osgearth_qt_windows/osgearth_qt_windows.cpp    |     7 +-
 src/applications/osgearth_seed/osgearth_seed.cpp   |    63 +-
 .../osgearth_sequencecontrol.cpp                   |     3 +-
 .../osgearth_shadercomp/osgearth_shadercomp.cpp    |    29 +-
 .../osgearth_shadergen/osgearth_shadergen.cpp      |     6 +-
 .../osgearth_sharedlayer/osgearth_sharedlayer.cpp  |    10 +-
 .../osgearth_silverlining.cpp                      |     1 +
 src/applications/osgearth_splat/osgearth_splat.cpp |   255 +-
 .../osgearth_terrainprofile.cpp                    |     9 +-
 src/applications/osgearth_tfs/osgearth_tfs.cpp     |     2 +-
 .../osgearth_tilesource/osgearth_tilesource.cpp    |     3 +-
 src/applications/osgearth_toc/osgearth_toc.cpp     |   407 +-
 .../osgearth_tracks/osgearth_tracks.cpp            |     4 +-
 .../osgearth_transform/osgearth_transform.cpp      |    46 +-
 .../osgearth_triton/osgearth_triton.cpp            |   168 +-
 .../CMakeLists.txt                                 |     4 +-
 .../osgearth_video.cpp}                            |    34 +-
 .../osgearth_viewer/osgearth_viewer.cpp            |    15 +-
 .../{osgEarthViewerIOS => }/AppDelegate.h          |     0
 .../AppDelegate.m => AppDelegate.mm}               |     0
 src/applications/osgearth_viewerIOS/CMakeLists.txt |   143 +
 .../EarthMultiTouchManipulator.cpp                 |     0
 .../EarthMultiTouchManipulator.h                   |     0
 .../StartViewerController.h                        |     0
 ...ViewerController.m => StartViewerController.mm} |    33 +-
 .../StartViewerController.xib                      |     0
 .../{osgEarthViewerIOS => }/ViewController.h       |     2 +
 .../ViewController.m => ViewController.mm}         |   103 +-
 .../osgearth_viewerIOS/ViewController.xib          |    39 +
 .../{osgEarthViewerIOS/main.m => main.mm}          |     0
 .../osgEarthViewerIOS-Info.plist                   |     0
 .../osgEarthViewerIOS.xcodeproj/project.pbxproj    |  1066 --
 .../project.xcworkspace/contents.xcworkspacedata   |     7 -
 .../ShaderGen/GLES2ShaderGenVisitor.cpp            |   581 -
 .../ShaderGen/GLES2ShaderGenVisitor.h              |    84 -
 .../osgEarthViewerIOS/ShaderGen/ShaderGenScene.h   |   227 -
 .../osgEarthViewerIOS/en.lproj/InfoPlist.strings   |     2 -
 .../en.lproj/ViewController_iPad.xib               |   125 -
 .../en.lproj/ViewController_iPhone.xib             |   124 -
 .../osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch |    14 -
 .../osgEarthViewerIOS/osgPlugins.h                 |    84 -
 .../osgEarthViewerIOS/osgearth_viewerIOS.cpp       |    66 -
 src/applications/osgearth_viewerIOS/osgPlugins.h   |   206 +
 src/applications/osgearth_wfs/osgearth_wfs.cpp     |    66 +-
 src/osgEarth/AlphaEffect                           |    73 -
 src/osgEarth/AlphaEffect.cpp                       |   151 -
 src/osgEarth/AlphaEffect.frag.glsl                 |    12 -
 src/osgEarth/AutoScale.cpp                         |   138 -
 src/osgEarth/Bounds.cpp                            |     2 +-
 src/osgEarth/CMakeLists.txt                        |    74 +-
 src/osgEarth/Cache                                 |    22 +-
 src/osgEarth/Cache.cpp                             |     6 +-
 src/osgEarth/CacheBin.cpp                          |    19 +-
 src/osgEarth/CacheEstimator.cpp                    |     3 -
 src/osgEarth/CachePolicy                           |    10 +-
 src/osgEarth/CachePolicy.cpp                       |    25 -
 src/osgEarth/CacheSeed                             |     9 +-
 src/osgEarth/CacheSeed.cpp                         |    46 +-
 src/osgEarth/Capabilities                          |    14 +-
 src/osgEarth/Capabilities.cpp                      |    41 +-
 src/osgEarth/ClampableNode                         |    41 +-
 src/osgEarth/ClampableNode.cpp                     |   138 +-
 src/osgEarth/Clamping                              |    73 +-
 src/osgEarth/Clamping.cpp                          |   148 +-
 src/osgEarth/ClampingTechnique                     |    10 +-
 src/osgEarth/ClampingTechnique.cpp                 |   159 +-
 src/osgEarth/ColorFilter                           |     8 +
 src/osgEarth/CompositeTileSource.cpp               |    86 +-
 src/osgEarth/Config                                |    62 +-
 src/osgEarth/Config.cpp                            |    30 +-
 src/osgEarth/Containers                            |    99 +-
 src/osgEarth/Cube                                  |     9 +-
 src/osgEarth/Cube.cpp                              |    53 +-
 src/osgEarth/CullingUtils                          |    44 +-
 src/osgEarth/CullingUtils.cpp                      |    82 +-
 src/osgEarth/DPLineSegmentIntersector              |     8 +-
 src/osgEarth/DPLineSegmentIntersector.cpp          |    14 +-
 src/osgEarth/DateTime                              |     2 +-
 src/osgEarth/DateTimeRange                         |    13 +
 src/osgEarth/DepthOffset                           |    31 +-
 src/osgEarth/DepthOffset.cpp                       |    30 +-
 src/osgEarth/DepthOffset.vert.glsl                 |    15 +-
 src/osgEarth/DrapeableNode                         |    19 +-
 src/osgEarth/DrapeableNode.cpp                     |    42 +-
 src/osgEarth/Draping.frag.glsl                     |    13 +-
 src/osgEarth/Draping.vert.glsl                     |     2 +-
 src/osgEarth/DrapingCullSet                        |    22 +-
 src/osgEarth/DrapingCullSet.cpp                    |    25 +-
 src/osgEarth/DrapingTechnique                      |     9 +-
 src/osgEarth/DrapingTechnique.cpp                  |    95 +-
 src/osgEarth/DrawInstanced                         |     4 +-
 src/osgEarth/DrawInstanced.cpp                     |    41 +-
 src/osgEarth/ElevationLOD                          |     3 +
 src/osgEarth/ElevationLayer                        |   118 +-
 src/osgEarth/ElevationLayer.cpp                    |   595 +-
 src/osgEarth/ElevationPool                         |   266 +
 src/osgEarth/ElevationPool.cpp                     |   513 +
 src/osgEarth/ElevationQuery                        |   140 +-
 src/osgEarth/ElevationQuery.cpp                    |   420 +-
 src/osgEarth/Endian                                |   178 +
 src/osgEarth/Export                                |     3 +
 src/osgEarth/Extension                             |    55 +-
 src/osgEarth/FadeEffect                            |     2 +-
 src/osgEarth/FadeEffect.cpp                        |     6 +-
 src/osgEarth/FileUtils.cpp                         |   113 +-
 src/osgEarth/GLSLChunker.cpp                       |     2 +-
 src/osgEarth/GPUClamping.frag.glsl                 |     3 +-
 src/osgEarth/GPUClamping.vert.glsl                 |   120 +-
 src/osgEarth/GPUClamping.vert.lib.glsl             |     2 +-
 src/osgEarth/GeoCommon                             |     6 +-
 src/osgEarth/GeoData                               |   143 +-
 src/osgEarth/GeoData.cpp                           |  1124 +-
 src/osgEarth/GeoTransform                          |    18 +-
 src/osgEarth/GeoTransform.cpp                      |   143 +-
 src/osgEarth/GeometryClamper                       |     7 +-
 src/osgEarth/GeometryClamper.cpp                   |   180 +-
 src/osgEarth/HTTPClient                            |     7 +-
 src/osgEarth/HTTPClient.cpp                        |  3362 +++---
 src/osgEarth/HeightFieldUtils                      |   102 +-
 src/osgEarth/HeightFieldUtils.cpp                  |   221 +-
 src/osgEarth/Horizon.cpp                           |     9 +-
 src/osgEarth/IOTypes                               |     8 +
 src/osgEarth/ImageLayer                            |   151 +-
 src/osgEarth/ImageLayer.cpp                        |   360 +-
 src/osgEarth/ImageMosaic.cpp                       |     3 +-
 src/osgEarth/ImageToHeightFieldConverter           |     2 +
 src/osgEarth/ImageToHeightFieldConverter.cpp       |    27 +
 src/osgEarth/ImageUtils                            |    22 +
 src/osgEarth/ImageUtils.cpp                        |   372 +-
 src/osgEarth/Instancing.vert.glsl                  |     2 +
 src/osgEarth/IntersectionPicker                    |     5 +
 src/osgEarth/LandCover                             |   250 +
 src/osgEarth/LandCover.cpp                         |   315 +
 src/osgEarth/LandCoverLayer                        |   108 +
 src/osgEarth/LandCoverLayer.cpp                    |   635 ++
 src/osgEarth/Layer                                 |   280 +-
 src/osgEarth/Layer.cpp                             |   307 +-
 src/osgEarth/LayerListener                         |   146 +
 src/osgEarth/Lighting                              |   137 +
 src/osgEarth/Lighting.cpp                          |   264 +
 src/osgEarth/LineFunctor                           |    12 -
 src/osgEarth/Locators.cpp                          |     4 +-
 src/osgEarth/Map                                   |   269 +-
 src/osgEarth/Map.cpp                               |  1052 +-
 src/osgEarth/MapCallback                           |    44 +-
 src/osgEarth/MapCallback.cpp                       |   109 +-
 src/osgEarth/MapFrame                              |    87 +-
 src/osgEarth/MapFrame.cpp                          |   264 +-
 src/osgEarth/MapInfo                               |    23 +-
 src/osgEarth/MapInfo.cpp                           |    27 +-
 src/osgEarth/MapModelChange                        |    20 +-
 src/osgEarth/MapNode                               |    89 +-
 src/osgEarth/MapNode.cpp                           |   540 +-
 src/osgEarth/MapNodeOptions                        |     7 +
 src/osgEarth/MapNodeOptions.cpp                    |     2 +-
 src/osgEarth/MapOptions                            |    17 +-
 src/osgEarth/MapOptions.cpp                        |    22 +-
 src/osgEarth/MaskLayer                             |    85 +-
 src/osgEarth/MaskLayer.cpp                         |   200 +-
 src/osgEarth/MaskNode                              |     1 +
 src/osgEarth/MaskSource                            |     3 +
 src/osgEarth/MaskSource.cpp                        |     7 +-
 src/osgEarth/MemCache                              |     3 -
 src/osgEarth/MemCache.cpp                          |     8 +-
 src/osgEarth/Mercator                              |    46 -
 src/osgEarth/Mercator.cpp                          |   149 -
 src/osgEarth/MetaTile                              |    77 +
 src/osgEarth/MetaTile.cpp                          |   122 +
 src/osgEarth/Metrics                               |   269 +
 src/osgEarth/Metrics.cpp                           |   496 +
 src/osgEarth/ModelLayer                            |   170 +-
 src/osgEarth/ModelLayer.cpp                        |   292 +-
 src/osgEarth/ModelSource                           |    41 +-
 src/osgEarth/ModelSource.cpp                       |   103 +-
 src/osgEarth/NodeUtils                             |    31 +-
 src/osgEarth/Notify                                |     2 +
 src/osgEarth/ObjectIndex                           |     7 +-
 src/osgEarth/ObjectIndex.cpp                       |     2 +
 src/osgEarth/OverlayDecorator                      |    14 +-
 src/osgEarth/OverlayDecorator.cpp                  |   138 +-
 src/osgEarth/OverlayNode                           |    94 -
 src/osgEarth/OverlayNode.cpp                       |   398 -
 src/osgEarth/PagedNode                             |    95 +
 src/osgEarth/PagedNode.cpp                         |   165 +
 src/osgEarth/PatchLayer                            |   121 +
 .../{TilePatchCallback.cpp => PatchLayer.cpp}      |    26 +-
 src/osgEarth/PhongLighting.frag.glsl               |   145 +
 src/osgEarth/PhongLighting.vert.glsl               |    20 +
 src/osgEarth/PhongLightingEffect                   |     5 +-
 src/osgEarth/PhongLightingEffect.cpp               |   160 +-
 src/osgEarth/PluginLoader                          |    83 +
 src/osgEarth/Profile                               |     5 +-
 src/osgEarth/Profile.cpp                           |    42 +-
 src/osgEarth/Profiler                              |     6 +
 src/osgEarth/Profiler.cpp                          |     7 +
 src/osgEarth/QuadTree                              |   199 -
 src/osgEarth/QuadTree.cpp                          |   825 --
 src/osgEarth/Registry                              |    65 +-
 src/osgEarth/Registry.cpp                          |   106 +-
 src/osgEarth/ResourceReleaser.cpp                  |     8 +-
 src/osgEarth/SceneGraphCallback                    |   115 +
 src/osgEarth/SceneGraphCallback.cpp                |   149 +
 src/osgEarth/ScreenSpaceLayout                     |    16 +-
 src/osgEarth/ScreenSpaceLayout.cpp                 |   150 +-
 src/osgEarth/ShaderFactory                         |    11 +-
 src/osgEarth/ShaderFactory.cpp                     |    86 +-
 src/osgEarth/ShaderGenerator                       |    10 +-
 src/osgEarth/ShaderGenerator.cpp                   |   258 +-
 src/osgEarth/ShaderLoader                          |     4 +
 src/osgEarth/ShaderLoader.cpp                      |   294 +-
 src/osgEarth/ShaderUtils                           |    76 -
 src/osgEarth/ShaderUtils.cpp                       |   370 +-
 src/osgEarth/Shaders                               |     2 +-
 src/osgEarth/Shaders.cpp.in                        |    13 +-
 src/osgEarth/Shadowing.cpp                         |     7 +-
 src/{osgEarthUtil => osgEarth}/SimplexNoise        |    22 +-
 src/{osgEarthUtil => osgEarth}/SimplexNoise.cpp    |    80 +-
 src/osgEarth/SpatialReference                      |    15 +-
 src/osgEarth/SpatialReference.cpp                  |   126 +-
 src/osgEarth/StateSetCache.cpp                     |     4 +-
 src/osgEarth/StateSetLOD                           |     5 +-
 src/osgEarth/StringUtils                           |     2 +-
 src/osgEarth/StringUtils.cpp                       |     6 +-
 src/osgEarth/Terrain                               |    72 +-
 src/osgEarth/Terrain.cpp                           |   143 +-
 src/osgEarth/TerrainEngineNode                     |    99 +-
 src/osgEarth/TerrainEngineNode.cpp                 |   224 +-
 src/osgEarth/TerrainEngineRequirements             |     2 +
 src/osgEarth/TerrainLayer                          |   305 +-
 src/osgEarth/TerrainLayer.cpp                      |   871 +-
 src/osgEarth/TerrainOptions                        |    22 +-
 src/osgEarth/TerrainOptions.cpp                    |    79 +-
 src/osgEarth/TerrainResources                      |   138 +
 src/osgEarth/TerrainResources.cpp                  |   262 +
 src/osgEarth/TerrainTileModel                      |    78 +-
 src/osgEarth/TerrainTileModel.cpp                  |    88 +-
 src/osgEarth/TerrainTileModelFactory               |    82 +-
 src/osgEarth/TerrainTileModelFactory.cpp           |   381 +-
 src/osgEarth/TerrainTileNode                       |    19 +-
 src/osgEarth/Tessellator.cpp                       |     4 +-
 src/osgEarth/TextureBufferSerializer.cpp           |    22 +
 src/osgEarth/TextureCompositor                     |    71 -
 src/osgEarth/TextureCompositor.cpp                 |    79 -
 src/osgEarth/ThreadingUtils                        |   333 +-
 src/osgEarth/ThreadingUtils.cpp                    |   170 +
 src/osgEarth/TileKeyDataStore                      |     1 +
 src/osgEarth/TilePatchCallback                     |    79 -
 src/osgEarth/TileRasterizer                        |   116 +
 src/osgEarth/TileRasterizer.cpp                    |   284 +
 src/osgEarth/TileSource                            |   156 +-
 src/osgEarth/TileSource.cpp                        |   395 +-
 src/osgEarth/TileVisitor.cpp                       |     8 +-
 src/osgEarth/TraversalData                         |     6 +-
 src/osgEarth/TraversalData.cpp                     |    48 +-
 src/osgEarth/URI                                   |    35 +
 src/osgEarth/URI.cpp                               |    32 +-
 src/osgEarth/Units                                 |    10 +-
 src/osgEarth/Units.cpp                             |     2 +-
 src/osgEarth/Utils                                 |    41 +-
 src/osgEarth/Utils.cpp                             |    53 +-
 src/osgEarth/Version                               |     4 +-
 src/osgEarth/VerticalDatum.cpp                     |     9 +-
 src/osgEarth/VideoLayer                            |    93 +
 src/osgEarth/VideoLayer.cpp                        |   155 +
 src/osgEarth/VirtualProgram                        |    22 +-
 src/osgEarth/VirtualProgram.cpp                    |   172 +-
 src/osgEarth/VisibleLayer                          |   108 +
 src/osgEarth/VisibleLayer.cpp                      |   177 +
 src/osgEarth/WrapperLayer                          |    74 +
 src/osgEarth/catch.hpp                             | 10663 +++++++++++++++++++
 src/osgEarth/tinyxml.cpp                           |     2 +
 src/osgEarth/tinyxml.h                             |   104 +-
 src/osgEarth/tinyxmlerror.cpp                      |     1 +
 src/osgEarth/tinyxmlparser.cpp                     |    12 +-
 src/osgEarthAnnotation/AnnotationEditing.cpp       |     2 +-
 src/osgEarthAnnotation/AnnotationExtension         |    61 -
 src/osgEarthAnnotation/AnnotationExtension.cpp     |    91 -
 src/osgEarthAnnotation/AnnotationLayer             |    98 +
 src/osgEarthAnnotation/AnnotationLayer.cpp         |    74 +
 src/osgEarthAnnotation/AnnotationNode              |     6 +
 src/osgEarthAnnotation/AnnotationNode.cpp          |    59 +-
 src/osgEarthAnnotation/AnnotationRegistry          |     5 +
 src/osgEarthAnnotation/AnnotationUtils.cpp         |    68 +-
 src/osgEarthAnnotation/BboxDrawable.cpp            |     2 -
 src/osgEarthAnnotation/CMakeLists.txt              |    13 +-
 src/osgEarthAnnotation/Common                      |     5 +-
 src/osgEarthAnnotation/Decoration                  |   120 -
 src/osgEarthAnnotation/Decoration.cpp              |   106 -
 src/osgEarthAnnotation/FeatureEditing.cpp          |     6 +-
 src/osgEarthAnnotation/FeatureNode                 |    55 +-
 src/osgEarthAnnotation/FeatureNode.cpp             |   126 +-
 src/osgEarthAnnotation/GeoPositionNode             |    12 +-
 src/osgEarthAnnotation/GeoPositionNode.cpp         |    41 +-
 .../GeoPositionNodeAutoScaler.cpp                  |    55 +-
 src/osgEarthAnnotation/HighlightDecoration         |    62 -
 src/osgEarthAnnotation/HighlightDecoration.cpp     |    95 -
 src/osgEarthAnnotation/ImageOverlay                |    16 +-
 src/osgEarthAnnotation/ImageOverlay.cpp            |   289 +-
 src/osgEarthAnnotation/LabelNode                   |     4 +
 src/osgEarthAnnotation/LabelNode.cpp               |    76 +-
 src/osgEarthAnnotation/LocalGeometryNode           |    17 +-
 src/osgEarthAnnotation/LocalGeometryNode.cpp       |   101 +-
 src/osgEarthAnnotation/ModelNode.cpp               |     7 +-
 src/osgEarthAnnotation/PlaceNode                   |     4 +
 src/osgEarthAnnotation/PlaceNode.cpp               |    60 +-
 src/osgEarthAnnotation/RectangleNode.cpp           |     4 +-
 src/osgEarthAnnotation/ScaleDecoration             |    54 -
 src/osgEarthAnnotation/TrackNode                   |     4 +
 src/osgEarthAnnotation/TrackNode.cpp               |    11 +-
 src/osgEarthDrivers/CMakeLists.txt                 |    63 +-
 src/osgEarthDrivers/agglite/AGGLiteOptions         |     4 +-
 .../agglite/AGGLiteRasterizerTileSource.cpp        |    29 +-
 src/osgEarthDrivers/arcgis/ArcGISOptions           |     8 +-
 .../arcgis_map_cache/CMakeLists.txt                |     3 -
 .../ReaderWriterArcGISMapCache.cpp                 |   147 -
 src/osgEarthDrivers/bing/BingOptions               |     6 +-
 src/osgEarthDrivers/bing/BingTileSource.cpp        |    10 +-
 .../bumpmap/BumpMap.frag.common.glsl               |     2 +-
 .../bumpmap/BumpMap.frag.progressive.glsl          |    14 +-
 .../bumpmap/BumpMap.frag.simple.glsl               |    14 +-
 src/osgEarthDrivers/bumpmap/BumpMap.vert.view.glsl |    10 +
 src/osgEarthDrivers/bumpmap/BumpMapOptions         |    12 +-
 .../bumpmap/BumpMapTerrainEffect.cpp               |     1 -
 .../cache_filesystem/FileSystemCache.cpp           |    36 +-
 src/osgEarthDrivers/cache_rocksdb/CMakeLists.txt   |     2 +
 src/osgEarthDrivers/colorramp/CMakeLists.txt       |     2 +-
 src/osgEarthDrivers/colorramp/ColorRampOptions     |     4 +-
 src/osgEarthDrivers/debug/DebugOptions             |     6 +-
 src/osgEarthDrivers/debug/DebugTileSource.cpp      |    18 +-
 src/osgEarthDrivers/detail/Detail.frag.glsl        |     4 +-
 src/osgEarthDrivers/detail/Detail.vert.view.glsl   |     3 +-
 src/osgEarthDrivers/detail/DetailOptions           |    10 +-
 src/osgEarthDrivers/detail/DetailTerrainEffect.cpp |     2 +-
 src/osgEarthDrivers/earth/EarthFileSerializer1.cpp |     7 +-
 src/osgEarthDrivers/earth/EarthFileSerializer2.cpp |   234 +-
 .../engine_byo/BYOTerrainEngineDriver.cpp          |    66 -
 .../engine_byo/BYOTerrainEngineNode                |    57 -
 .../engine_byo/BYOTerrainEngineNode.cpp            |    81 -
 .../engine_byo/BYOTerrainEngineOptions             |    91 -
 src/osgEarthDrivers/engine_byo/CMakeLists.txt      |    20 -
 src/osgEarthDrivers/engine_byo/Common              |    24 -
 src/osgEarthDrivers/engine_mp/HeightFieldCache.cpp |     2 +-
 .../engine_mp/MPEngine.NormalMap.frag.glsl         |     3 +-
 .../engine_mp/MPEngine.NormalMap.vert.glsl         |     1 +
 src/osgEarthDrivers/engine_mp/MPEngine.frag.glsl   |    19 +-
 src/osgEarthDrivers/engine_mp/MPGeometry           |     5 +
 src/osgEarthDrivers/engine_mp/MPGeometry.cpp       |   104 +-
 .../engine_mp/MPTerrainEngineDriver.cpp            |     8 +-
 src/osgEarthDrivers/engine_mp/MPTerrainEngineNode  |    15 +-
 .../engine_mp/MPTerrainEngineNode.cpp              |   232 +-
 .../engine_mp/MPTerrainEngineOptions               |    40 +-
 .../engine_mp/SingleKeyNodeFactory.cpp             |    64 +-
 src/osgEarthDrivers/engine_mp/TileModel.cpp        |     4 +-
 src/osgEarthDrivers/engine_mp/TileModelCompiler    |     6 +-
 .../engine_mp/TileModelCompiler.cpp                |    51 +-
 src/osgEarthDrivers/engine_mp/TileModelFactory.cpp |    41 +-
 src/osgEarthDrivers/engine_mp/TileNode             |     6 +-
 src/osgEarthDrivers/engine_mp/TileNode.cpp         |     8 +-
 src/osgEarthDrivers/engine_mp/TileNodeRegistry     |     4 +-
 src/osgEarthDrivers/engine_mp/TileNodeRegistry.cpp |    11 +-
 src/osgEarthDrivers/engine_mp/TilePagedLOD         |    17 +-
 src/osgEarthDrivers/engine_mp/TilePagedLOD.cpp     |    67 +-
 src/osgEarthDrivers/engine_rex/CMakeLists.txt      |    16 +-
 src/osgEarthDrivers/engine_rex/DrawState           |   152 +
 src/osgEarthDrivers/engine_rex/DrawState.cpp       |    79 +
 src/osgEarthDrivers/engine_rex/DrawTileCommand     |   114 +
 src/osgEarthDrivers/engine_rex/DrawTileCommand.cpp |   155 +
 src/osgEarthDrivers/engine_rex/EngineContext       |    47 +-
 src/osgEarthDrivers/engine_rex/EngineContext.cpp   |    83 +-
 src/osgEarthDrivers/engine_rex/GeometryPool        |   121 +-
 src/osgEarthDrivers/engine_rex/GeometryPool.cpp    |   549 +-
 src/osgEarthDrivers/engine_rex/LayerDrawable       |    87 +
 src/osgEarthDrivers/engine_rex/LayerDrawable.cpp   |    96 +
 src/osgEarthDrivers/engine_rex/LoadTileData        |    23 +-
 src/osgEarthDrivers/engine_rex/LoadTileData.cpp    |   275 +-
 src/osgEarthDrivers/engine_rex/Loader              |    33 +-
 src/osgEarthDrivers/engine_rex/Loader.cpp          |   153 +-
 src/osgEarthDrivers/engine_rex/MPTexture           |   106 -
 src/osgEarthDrivers/engine_rex/MPTexture.cpp       |   171 -
 src/osgEarthDrivers/engine_rex/MaskGenerator       |     5 +-
 src/osgEarthDrivers/engine_rex/MaskGenerator.cpp   |    40 +-
 src/osgEarthDrivers/engine_rex/RenderBindings      |    45 +-
 .../engine_rex/RexEngine.Morphing.vert.glsl        |    21 +-
 .../engine_rex/RexEngine.NormalMap.frag.glsl       |    24 +-
 .../engine_rex/RexEngine.NormalMap.vert.glsl       |    15 +-
 .../engine_rex/RexEngine.SDK.vert.glsl             |    26 +-
 .../engine_rex/RexEngine.elevation.glsl            |    33 +
 src/osgEarthDrivers/engine_rex/RexEngine.frag.glsl |    58 +-
 src/osgEarthDrivers/engine_rex/RexEngine.gs.glsl   |     1 +
 src/osgEarthDrivers/engine_rex/RexEngine.tcs.glsl  |     1 +
 src/osgEarthDrivers/engine_rex/RexEngine.vert.glsl |     3 +-
 .../engine_rex/RexEngine.vert.view.glsl            |    27 +-
 .../engine_rex/RexTerrainEngineNode                |    99 +-
 .../engine_rex/RexTerrainEngineNode.cpp            |  1095 +-
 .../engine_rex/RexTerrainEngineOptions             |    79 +-
 src/osgEarthDrivers/engine_rex/SelectionInfo       |    28 +-
 src/osgEarthDrivers/engine_rex/SelectionInfo.cpp   |    98 +-
 src/osgEarthDrivers/engine_rex/Shaders             |     1 +
 src/osgEarthDrivers/engine_rex/Shaders.cpp.in      |     3 +
 src/osgEarthDrivers/engine_rex/SurfaceNode         |    21 +-
 src/osgEarthDrivers/engine_rex/SurfaceNode.cpp     |    39 +-
 src/osgEarthDrivers/engine_rex/TerrainCuller       |    94 +
 src/osgEarthDrivers/engine_rex/TerrainCuller.cpp   |   274 +
 src/osgEarthDrivers/engine_rex/TerrainRenderData   |    75 +
 .../engine_rex/TerrainRenderData.cpp               |   131 +
 src/osgEarthDrivers/engine_rex/TileDrawable        |   143 +-
 src/osgEarthDrivers/engine_rex/TileDrawable.cpp    |   626 +-
 src/osgEarthDrivers/engine_rex/TileNode            |   100 +-
 src/osgEarthDrivers/engine_rex/TileNode.cpp        |   952 +-
 src/osgEarthDrivers/engine_rex/TileNodeRegistry    |    10 +-
 .../engine_rex/TileNodeRegistry.cpp                |    90 +-
 src/osgEarthDrivers/engine_rex/TileRenderModel     |   171 +
 src/osgEarthDrivers/engine_rex/Unloader            |    12 +-
 src/osgEarthDrivers/engine_rex/Unloader.cpp        |    12 +-
 src/osgEarthDrivers/fastdxt/CMakeLists.txt         |    11 +-
 .../fastdxt/FastDXTImageProcessor.cpp              |     8 +-
 src/osgEarthDrivers/fastdxt/util.cpp               |     4 +-
 src/osgEarthDrivers/fastdxt/util.h                 |     4 +-
 .../feature_elevation/FeatureElevationOptions      |     6 +-
 .../ReaderWriterFeatureElevation.cpp               |    29 +-
 .../feature_mapnikvectortiles/FeatureSourceMVT.cpp |     4 +-
 .../feature_mapnikvectortiles/MVTFeatureOptions    |     2 +-
 src/osgEarthDrivers/feature_ogr/FeatureCursorOGR   |    16 +-
 .../feature_ogr/FeatureCursorOGR.cpp               |    31 +-
 .../feature_ogr/FeatureSourceOGR.cpp               |     2 +-
 src/osgEarthDrivers/feature_ogr/OGRFeatureOptions  |    16 +-
 .../feature_raster/FeatureSourceRaster.cpp         |    14 +-
 .../feature_raster/RasterFeatureOptions            |     6 +-
 .../feature_tfs/FeatureSourceTFS.cpp               |    30 +-
 src/osgEarthDrivers/feature_tfs/TFSFeatureOptions  |    10 +-
 .../feature_wfs/FeatureSourceWFS.cpp               |    53 +-
 src/osgEarthDrivers/feature_wfs/WFSFeatureOptions  |    14 +-
 src/osgEarthDrivers/feature_xyz/CMakeLists.txt     |    18 +
 .../feature_xyz/FeatureSourceXYZ.cpp               |   416 +
 .../feature_xyz/XYZFeatureOptions}                 |    48 +-
 .../IntersectFeatureFilter.cpp                     |    12 +-
 .../featurefilter_join/JoinFeatureFilter.cpp       |     2 +
 src/osgEarthDrivers/gdal/GDALOptions               |    16 +-
 src/osgEarthDrivers/gdal/ReaderWriterGDAL.cpp      |   799 +-
 src/osgEarthDrivers/kml/KMLReader.cpp              |    19 +-
 src/osgEarthDrivers/kml/KML_Placemark.cpp          |    22 +-
 src/osgEarthDrivers/kml/KML_PolyStyle.cpp          |    49 +-
 src/osgEarthDrivers/kml/rapidxml_ext.hpp           |    13 +-
 .../label_annotation/AnnotationLabelSource.cpp     |     1 +
 .../mapinspector/MapInspectorExtension             |     9 +-
 .../mapinspector/MapInspectorExtension.cpp         |    21 +-
 src/osgEarthDrivers/mapinspector/MapInspectorUI    |     2 +
 .../mapinspector/MapInspectorUI.cpp                |    48 +-
 .../mask_feature/FeatureMaskOptions                |     2 +-
 .../mask_feature/FeatureMaskSource.cpp             |     3 +
 src/osgEarthDrivers/mbtiles/MBTilesOptions         |     9 +-
 src/osgEarthDrivers/mbtiles/MBTilesTileSource.cpp  |   102 +-
 .../model_feature_geom/FeatureGeomModelOptions     |    17 +-
 .../model_feature_geom/FeatureGeomModelSource.cpp  |     2 +-
 .../model_simple/SimpleModelOptions                |    14 +-
 .../model_simple/SimpleModelSource.cpp             |     2 +-
 src/osgEarthDrivers/noise/CMakeLists.txt           |    24 -
 src/osgEarthDrivers/noise/NoiseExtension           |    74 -
 src/osgEarthDrivers/noise/NoiseExtension.cpp       |    80 -
 src/osgEarthDrivers/noise/NoiseOptions             |    82 -
 src/osgEarthDrivers/noise/NoiseTerrainEffect       |    65 -
 src/osgEarthDrivers/noise/NoiseTerrainEffect.cpp   |   167 -
 .../ocean_simple/ElevationProxyImageLayer          |     2 +-
 .../ocean_simple/ElevationProxyImageLayer.cpp      |    36 +-
 .../ocean_simple/SimpleOcean.FS.glsl               |    67 +-
 .../ocean_simple/SimpleOcean.VS.glsl               |    46 +-
 .../ocean_simple/SimpleOceanNode.cpp               |    70 +-
 .../ocean_simple/SimpleOceanOptions                |    22 +-
 src/osgEarthDrivers/ocean_triton/TritonDriver.cpp  |     4 +-
 src/osgEarthDrivers/osg/OSGOptions                 |    15 +-
 src/osgEarthDrivers/osg/OSGTileSource.cpp          |    38 +-
 src/osgEarthDrivers/quadkey/CMakeLists.txt         |    15 -
 src/osgEarthDrivers/quadkey/QuadKeyOptions         |    82 -
 .../quadkey/ReaderWriterQuadKey.cpp                |   199 -
 src/osgEarthDrivers/refresh/CMakeLists.txt         |    13 -
 .../refresh/ReaderWriterRefresh.cpp                |   251 -
 src/osgEarthDrivers/refresh/RefreshOptions         |    85 -
 .../script_engine_duktape/DuktapeEngine.cpp        |     2 +-
 .../script_engine_javascriptcore/CMakeLists.txt    |    26 -
 .../script_engine_javascriptcore/JSWrappers        |    33 -
 .../script_engine_javascriptcore/JSWrappers.cpp    |    87 -
 .../JavaScriptCoreEngine                           |    59 -
 .../JavaScriptCoreEngine.cpp                       |   126 -
 .../JavaScriptCoreEngineFactory.cpp                |    50 -
 .../script_engine_v8/CMakeLists.txt                |    29 -
 src/osgEarthDrivers/script_engine_v8/JSWrappers    |   233 -
 .../script_engine_v8/JSWrappers.cpp                |  1294 ---
 .../script_engine_v8/JavascriptEngineV8            |    74 -
 .../script_engine_v8/JavascriptEngineV8.cpp        |   474 -
 .../script_engine_v8/JavascriptEngineV8Factory.cpp |    50 -
 src/osgEarthDrivers/script_engine_v8/V8Util        |    79 -
 src/osgEarthDrivers/sky_gl/CMakeLists.txt          |     1 -
 src/osgEarthDrivers/sky_gl/GLSkyExtension.cpp      |    41 +-
 src/osgEarthDrivers/sky_gl/GLSkyNode               |     3 +-
 src/osgEarthDrivers/sky_gl/GLSkyNode.cpp           |    38 +-
 src/osgEarthDrivers/sky_gl/GLSkyShaders            |   130 -
 .../sky_silverlining/SilverLiningDriver.cpp        |     1 +
 .../sky_simple/SimpleSky.Atmosphere.frag.glsl      |    17 +-
 .../sky_simple/SimpleSky.Atmosphere.vert.glsl      |    20 +-
 .../sky_simple/SimpleSky.Ground.ONeil.frag.glsl    |   184 +-
 .../sky_simple/SimpleSky.Ground.ONeil.vert.glsl    |    56 +-
 .../sky_simple/SimpleSky.Moon.frag.glsl            |     6 +-
 .../sky_simple/SimpleSky.Moon.vert.glsl            |     5 +-
 .../sky_simple/SimpleSky.Stars.GLES.frag.glsl      |    11 +-
 .../sky_simple/SimpleSky.Stars.GLES.vert.glsl      |     9 +-
 .../sky_simple/SimpleSky.Stars.frag.glsl           |     5 +-
 .../sky_simple/SimpleSky.Stars.vert.glsl           |    10 +-
 .../sky_simple/SimpleSky.Sun.frag.glsl             |    13 +-
 .../sky_simple/SimpleSky.Sun.vert.glsl             |     2 +-
 .../sky_simple/SimpleSkyExtension.cpp              |    10 +-
 src/osgEarthDrivers/sky_simple/SimpleSkyNode       |     3 +-
 src/osgEarthDrivers/sky_simple/SimpleSkyNode.cpp   |    70 +-
 src/osgEarthDrivers/sky_simple/SimpleSkyOptions    |    22 +-
 src/osgEarthDrivers/skyview/SkyViewOptions         |     2 +-
 src/osgEarthDrivers/splat_mask/CMakeLists.txt      |    14 -
 src/osgEarthDrivers/splat_mask/SplatMaskDriver.cpp |   205 -
 src/osgEarthDrivers/splat_mask/SplatMaskOptions    |    79 -
 .../template_matclass/CMakeLists.txt               |    16 -
 .../template_matclass/TemplateMatClassDriver.cpp   |   167 -
 .../template_matclass/TemplateMatClassOptions      |    83 -
 .../terrainshader/TerrainShaderExtension.cpp       |     8 +-
 src/osgEarthDrivers/tilecache/CMakeLists.txt       |    17 -
 .../tilecache/ReaderWriterTileCache.cpp            |   131 -
 src/osgEarthDrivers/tilecache/TileCacheOptions     |    79 -
 src/osgEarthDrivers/tileindex/TileIndexOptions     |     2 +-
 src/osgEarthDrivers/tileservice/CMakeLists.txt     |    15 -
 .../tileservice/ReaderWriterTileService.cpp        |   138 -
 src/osgEarthDrivers/tileservice/TileServiceOptions |    79 -
 src/osgEarthDrivers/tms/TMSOptions                 |     6 +-
 src/osgEarthDrivers/tms/TMSTileSource.cpp          |    30 +-
 src/osgEarthDrivers/vpb/VPBOptions                 |    18 +-
 src/osgEarthDrivers/wcs/WCS11Source.cpp            |     4 +-
 src/osgEarthDrivers/wcs/WCSOptions                 |    12 +-
 src/osgEarthDrivers/wms/ReaderWriterWMS.cpp        |    15 +-
 src/osgEarthDrivers/wms/WMSOptions                 |    29 +-
 src/osgEarthDrivers/xyz/ReaderWriterXYZ.cpp        |   216 +-
 src/osgEarthDrivers/xyz/XYZOptions                 |    14 +-
 src/osgEarthDrivers/yahoo/CMakeLists.txt           |     9 -
 src/osgEarthDrivers/yahoo/ReaderWriterYahoo.cpp    |   151 -
 src/osgEarthDrivers/yahoo/YahooOptions             |    69 -
 .../feature_mapnikvectortiles/CMakeLists.txt       |    29 -
 .../feature_mapnikvectortiles/FeatureSourceMVT.cpp |   446 -
 .../feature_mapnikvectortiles/vector_tile.proto    |    92 -
 src/osgEarthExtensions/CMakeLists.txt              |    29 -
 src/osgEarthFeatures/AltitudeFilter.cpp            |   126 +-
 src/osgEarthFeatures/BufferFilter.cpp              |     1 +
 src/osgEarthFeatures/BuildGeometryFilter           |    27 +-
 src/osgEarthFeatures/BuildGeometryFilter.cpp       |   564 +-
 src/osgEarthFeatures/BuildTextFilter               |     2 -
 src/osgEarthFeatures/BuildTextFilter.cpp           |     2 +-
 src/osgEarthFeatures/CMakeLists.txt                |    42 +-
 src/osgEarthFeatures/CentroidFilter.cpp            |     1 +
 src/osgEarthFeatures/ConvertTypeFilter.cpp         |     2 -
 src/osgEarthFeatures/ExtrudeGeometryFilter         |     9 +-
 src/osgEarthFeatures/ExtrudeGeometryFilter.cpp     |    13 +-
 src/osgEarthFeatures/Feature                       |    31 +-
 src/osgEarthFeatures/Feature.cpp                   |    94 +-
 src/osgEarthFeatures/FeatureCursor                 |    18 +-
 src/osgEarthFeatures/FeatureCursor.cpp             |    27 +-
 src/osgEarthFeatures/FeatureDisplayLayout          |     9 +-
 src/osgEarthFeatures/FeatureDisplayLayout.cpp      |     5 +-
 src/osgEarthFeatures/FeatureDrawSet                |    91 -
 src/osgEarthFeatures/FeatureDrawSet.cpp            |   257 -
 src/osgEarthFeatures/FeatureListSource             |     7 +-
 src/osgEarthFeatures/FeatureListSource.cpp         |     4 +-
 src/osgEarthFeatures/FeatureMaskLayer              |   124 +
 src/osgEarthFeatures/FeatureMaskLayer.cpp          |   209 +
 src/osgEarthFeatures/FeatureModelGraph             |    80 +-
 src/osgEarthFeatures/FeatureModelGraph.cpp         |   519 +-
 src/osgEarthFeatures/FeatureModelLayer             |   130 +
 src/osgEarthFeatures/FeatureModelLayer.cpp         |   255 +
 src/osgEarthFeatures/FeatureModelSource            |    72 +-
 src/osgEarthFeatures/FeatureModelSource.cpp        |   138 +-
 src/osgEarthFeatures/FeatureRasterizer.cpp         |    20 +-
 src/osgEarthFeatures/FeatureSource                 |    20 +-
 src/osgEarthFeatures/FeatureSource.cpp             |    39 +-
 src/osgEarthFeatures/FeatureSourceIndexNode.cpp    |     1 +
 src/osgEarthFeatures/FeatureSourceLayer            |    94 +
 src/osgEarthFeatures/FeatureSourceLayer.cpp        |    94 +
 src/osgEarthFeatures/FeatureTileSource             |     2 +-
 src/osgEarthFeatures/FeatureTileSource.cpp         |    12 +-
 src/osgEarthFeatures/Filter                        |    30 +-
 src/osgEarthFeatures/Filter.cpp                    |    11 +-
 src/osgEarthFeatures/FilterContext                 |    30 +-
 src/osgEarthFeatures/FilterContext.cpp             |    55 +-
 src/osgEarthFeatures/GPULines                      |    96 +
 src/osgEarthFeatures/GPULines.cpp                  |   206 +
 src/osgEarthFeatures/GPULinesScreenProj.glsl       |   181 +
 src/osgEarthFeatures/GeometryCompiler              |    28 +-
 src/osgEarthFeatures/GeometryCompiler.cpp          |    52 +-
 src/osgEarthFeatures/LabelSource                   |     3 +-
 src/osgEarthFeatures/LabelSource.cpp               |    11 +-
 src/osgEarthFeatures/MVT.cpp                       |   339 +-
 src/osgEarthFeatures/OgrUtils.cpp                  |    97 +-
 src/osgEarthFeatures/OptimizerHints                |    78 -
 src/osgEarthFeatures/OptimizerHints.cpp            |    59 -
 src/osgEarthFeatures/PolygonizeLines               |     9 +-
 src/osgEarthFeatures/PolygonizeLines.cpp           |    61 +-
 src/osgEarthFeatures/ResampleFilter                |     1 +
 src/osgEarthFeatures/ResampleFilter.cpp            |     2 +-
 src/osgEarthFeatures/ScaleFilter.cpp               |     1 +
 src/osgEarthFeatures/ScatterFilter.cpp             |     1 +
 src/osgEarthFeatures/ScriptEngine.cpp              |     7 +-
 src/osgEarthFeatures/ScriptFilter.cpp              |     1 +
 src/osgEarthFeatures/Session                       |   128 +-
 src/osgEarthFeatures/Session.cpp                   |    96 +-
 src/{osgEarth => osgEarthFeatures}/Shaders         |    21 +-
 src/osgEarthFeatures/Shaders.cpp.in                |    13 +
 src/osgEarthFeatures/SubstituteModelFilter         |     6 +
 src/osgEarthFeatures/SubstituteModelFilter.cpp     |    16 +-
 src/osgEarthFeatures/TessellateOperator            |     2 +
 src/osgEarthFeatures/TessellateOperator.cpp        |    28 +-
 src/osgEarthFeatures/TextSymbolizer.cpp            |    13 +-
 src/osgEarthFeatures/TransformFilter.cpp           |     2 +
 src/osgEarthFeatures/VirtualFeatureSource          |     8 +-
 src/osgEarthFeatures/VirtualFeatureSource.cpp      |    24 +-
 src/osgEarthProcedural/CMakeLists.txt              |    67 -
 src/osgEarthProcedural/Common                      |    32 -
 src/osgEarthProcedural/CoverageLegend              |    99 -
 src/osgEarthProcedural/CoverageLegend.cpp          |    77 -
 src/osgEarthProcedural/Export                      |    74 -
 src/osgEarthProcedural/Shaders                     |    31 -
 src/osgEarthProcedural/Shaders.cpp.in              |     9 -
 src/osgEarthProcedural/SimplexNoise                |   159 -
 src/osgEarthProcedural/SimplexNoise.cpp            |   556 -
 src/osgEarthProcedural/SplatCatalog                |   161 -
 src/osgEarthProcedural/SplatCatalog.cpp            |   343 -
 src/osgEarthQt/CMakeLists.txt                      |   340 +-
 src/osgEarthQt/DataManager                         |    14 +-
 src/osgEarthQt/DataManager.cpp                     |    42 +-
 src/osgEarthQt/GuiActions                          |     1 +
 src/osgEarthQt/LayerManagerWidget                  |     3 +
 src/osgEarthQt/LayerManagerWidget.cpp              |    30 +-
 src/osgEarthQt/MapCatalogWidget.cpp                |    15 +-
 .../SilverLiningContextNode.cpp                    |     1 +
 src/osgEarthSilverLining/SilverLiningNode          |     2 +-
 src/osgEarthSilverLining/SilverLiningNode.cpp      |    17 +-
 src/osgEarthSplat/CMakeLists.txt                   |    27 +-
 src/osgEarthSplat/Coverage                         |     4 +-
 src/osgEarthSplat/Coverage.cpp                     |     3 +-
 src/osgEarthSplat/Export                           |     2 +-
 src/osgEarthSplat/GroundCover                      |   260 +
 src/osgEarthSplat/GroundCover.FS.glsl              |    29 +
 .../{LandCover.GS.glsl => GroundCover.GS.glsl}     |   243 +-
 .../{LandCover.TCS.glsl => GroundCover.TCS.glsl}   |    24 +-
 .../{LandCover.TES.glsl => GroundCover.TES.glsl}   |     6 +-
 src/osgEarthSplat/GroundCover.cpp                  |   448 +
 src/osgEarthSplat/GroundCoverLayer                 |   163 +
 src/osgEarthSplat/GroundCoverLayer.cpp             |   414 +
 src/osgEarthSplat/LandCover                        |   445 -
 src/osgEarthSplat/LandCover.FS.glsl                |    49 -
 src/osgEarthSplat/LandCover.cpp                    |   438 -
 src/osgEarthSplat/LandCoverTerrainEffect           |    87 -
 src/osgEarthSplat/LandCoverTerrainEffect.cpp       |   238 -
 src/osgEarthSplat/LandCoverTilePatchCallback       |    58 -
 src/osgEarthSplat/LandCoverTilePatchCallback.cpp   |    89 -
 src/osgEarthSplat/LandUseTileSource                |     6 +-
 src/osgEarthSplat/LandUseTileSource.cpp            |     8 +-
 src/osgEarthSplat/NoiseTextureFactory.cpp          |     5 +-
 src/osgEarthSplat/RoadSurfaceLayer                 |   160 +
 src/osgEarthSplat/RoadSurfaceLayer.cpp             |   407 +
 src/osgEarthSplat/Splat.Noise.glsl                 |     1 +
 src/osgEarthSplat/Splat.frag.common.glsl           |    35 -
 src/osgEarthSplat/Splat.frag.glsl                  |   162 +-
 src/osgEarthSplat/Splat.util.glsl                  |     1 +
 src/osgEarthSplat/Splat.vert.model.glsl            |    14 +
 src/osgEarthSplat/Splat.vert.view.glsl             |     7 +-
 src/osgEarthSplat/SplatCatalog                     |     3 +-
 src/osgEarthSplat/SplatExtension                   |    12 +-
 src/osgEarthSplat/SplatExtension.cpp               |    70 +-
 src/osgEarthSplat/SplatLayer                       |   139 +
 src/osgEarthSplat/SplatLayer.cpp                   |   352 +
 src/osgEarthSplat/SplatOptions                     |     6 +-
 src/osgEarthSplat/SplatShaders                     |    12 +-
 src/osgEarthSplat/SplatShaders.cpp.in              |    21 +-
 src/osgEarthSplat/SplatTerrainEffect               |   111 -
 src/osgEarthSplat/SplatTerrainEffect.cpp           |   232 -
 src/osgEarthSplat/Surface                          |    13 +-
 src/osgEarthSplat/Surface.cpp                      |   113 +-
 src/osgEarthSplat/Zone                             |   106 +-
 src/osgEarthSplat/Zone.cpp                         |    94 +-
 src/osgEarthSymbology/GEOS                         |     6 +
 src/osgEarthSymbology/GEOS.cpp                     |    21 +-
 src/osgEarthSymbology/Geometry                     |    10 +
 src/osgEarthSymbology/Geometry.cpp                 |    48 +-
 src/osgEarthSymbology/InstanceResource.cpp         |     2 +-
 src/osgEarthSymbology/LineSymbol                   |    13 +-
 src/osgEarthSymbology/LineSymbol.cpp               |    24 +-
 src/osgEarthSymbology/MarkerSymbolizer.cpp         |     4 +-
 src/osgEarthSymbology/MeshConsolidator.cpp         |     5 +-
 src/osgEarthSymbology/MeshSubdivider.cpp           |    12 +-
 src/osgEarthSymbology/ModelResource                |     2 +-
 src/osgEarthSymbology/ModelResource.cpp            |    14 +-
 src/osgEarthSymbology/PolygonSymbol                |     7 +
 src/osgEarthSymbology/PolygonSymbol.cpp            |     9 +-
 src/osgEarthSymbology/Query                        |    10 +-
 src/osgEarthSymbology/Query.cpp                    |    18 +-
 src/osgEarthSymbology/RenderSymbol                 |    10 +
 src/osgEarthSymbology/RenderSymbol.cpp             |    36 +-
 src/osgEarthSymbology/Resource                     |     9 +
 src/osgEarthSymbology/ResourceCache                |     6 +
 src/osgEarthSymbology/ResourceCache.cpp            |    30 +
 src/osgEarthSymbology/ResourceLibrary              |     1 +
 src/osgEarthSymbology/ResourceLibrary.cpp          |    10 +-
 src/osgEarthSymbology/Skins.cpp                    |    48 +-
 src/osgEarthSymbology/Style                        |     4 +
 src/osgEarthSymbology/StyleSheet                   |     4 +
 src/osgEarthSymbology/StyleSheet.cpp               |    10 +-
 src/osgEarthSymbology/Symbol                       |    13 +-
 src/osgEarthSymbology/TextSymbol                   |     5 -
 src/osgEarthSymbology/TextSymbol.cpp               |    10 -
 src/osgEarthTriton/CMakeLists.txt                  |     2 +
 src/osgEarthTriton/TritonAPIWrapper                |     8 +-
 src/osgEarthTriton/TritonContext                   |    24 +-
 src/osgEarthTriton/TritonContext.cpp               |    46 +-
 src/osgEarthTriton/TritonDrawable                  |     8 +-
 src/osgEarthTriton/TritonDrawable.cpp              |   101 +-
 src/osgEarthTriton/{TritonOptions => TritonLayer}  |    83 +-
 src/osgEarthTriton/TritonLayer.cpp                 |    96 +
 src/osgEarthTriton/TritonNode                      |    26 +-
 src/osgEarthTriton/TritonNode.cpp                  |   125 +-
 src/osgEarthTriton/TritonOptions                   |    16 +-
 src/osgEarthUtil/AnnotationEvents.cpp              |     1 +
 src/osgEarthUtil/AtlasBuilder.cpp                  |    17 +-
 src/osgEarthUtil/AutoClipPlaneHandler.cpp          |     4 -
 src/osgEarthUtil/CMakeLists.txt                    |    57 +-
 src/osgEarthUtil/ContourMap                        |     9 +-
 src/osgEarthUtil/ContourMap.cpp                    |     7 +-
 src/osgEarthUtil/ContourMap.frag.glsl              |     6 +-
 src/osgEarthUtil/ContourMap.vert.glsl              |     6 +-
 src/osgEarthUtil/Controls                          |    10 +-
 src/osgEarthUtil/Controls.cpp                      |    81 +-
 src/osgEarthUtil/EarthManipulator                  |    30 +-
 src/osgEarthUtil/EarthManipulator.cpp              |    97 +-
 src/osgEarthUtil/ExampleResources.cpp              |    37 +-
 src/osgEarthUtil/FeatureQueryTool                  |    57 -
 src/osgEarthUtil/FeatureQueryTool.cpp              |    35 -
 src/osgEarthUtil/FlatteningLayer                   |   217 +
 src/osgEarthUtil/FlatteningLayer.cpp               |   985 ++
 src/osgEarthUtil/Fog.cpp                           |     2 +-
 src/osgEarthUtil/Fog.frag.glsl                     |     4 +-
 src/osgEarthUtil/Fog.vert.glsl                     |     4 +-
 src/osgEarthUtil/FractalElevationLayer             |   154 +
 src/osgEarthUtil/FractalElevationLayer.cpp         |   378 +
 src/osgEarthUtil/GARSGraticule                     |   115 +
 src/osgEarthUtil/GARSGraticule.cpp                 |   382 +
 src/osgEarthUtil/GeodeticGraticule                 |   256 +-
 src/osgEarthUtil/GeodeticGraticule.cpp             |   860 +-
 src/osgEarthUtil/Graticule.frag.glsl               |    27 +-
 src/osgEarthUtil/Graticule.vert.glsl               |     8 +-
 src/osgEarthUtil/GraticuleExtension                |    75 -
 src/osgEarthUtil/GraticuleExtension.cpp            |    83 -
 src/osgEarthUtil/GraticuleNode                     |   118 -
 src/osgEarthUtil/GraticuleNode.cpp                 |   425 -
 src/osgEarthUtil/GraticuleOptions                  |   114 -
 src/osgEarthUtil/GraticuleTerrainEffect            |    61 -
 src/osgEarthUtil/GraticuleTerrainEffect.cpp        |    87 -
 src/osgEarthUtil/HTM                               |    52 +-
 src/osgEarthUtil/HTM.cpp                           |   162 +-
 src/osgEarthUtil/LODBlending.cpp                   |    42 +-
 src/osgEarthUtil/LinearLineOfSight.cpp             |    18 +-
 src/osgEarthUtil/LogDepthBuffer.VertOnly.vert.glsl |     4 +-
 src/osgEarthUtil/LogDepthBuffer.frag.glsl          |    12 +-
 src/osgEarthUtil/LogDepthBuffer.vert.glsl          |     7 +-
 src/osgEarthUtil/LogarithmicDepthBuffer            |     3 +
 src/osgEarthUtil/LogarithmicDepthBuffer.cpp        |     8 +-
 src/osgEarthUtil/MGRSGraticule                     |   123 +-
 src/osgEarthUtil/MGRSGraticule.cpp                 |  1357 ++-
 src/osgEarthUtil/MouseCoordsTool.cpp               |    22 +-
 src/osgEarthUtil/MultiElevationLayer               |   100 +
 src/osgEarthUtil/MultiElevationLayer.cpp           |   179 +
 src/osgEarthUtil/NightColorFilter.cpp              |     7 +-
 src/osgEarthUtil/ObjectLocator                     |   204 -
 src/osgEarthUtil/ObjectLocator.cpp                 |   288 -
 src/osgEarthUtil/PolyhedralLineOfSight.cpp         |     6 +-
 src/osgEarthUtil/RTTPicker                         |    28 +-
 src/osgEarthUtil/RTTPicker.cpp                     |   215 +-
 src/osgEarthUtil/RadialLineOfSight.cpp             |    26 +-
 src/osgEarthUtil/Shaders                           |     5 +-
 src/osgEarthUtil/Shaders.cpp.in                    |     6 +
 src/osgEarthUtil/Shadowing.cpp                     |     3 +-
 src/osgEarthUtil/Shadowing.frag.glsl               |    24 +-
 src/osgEarthUtil/Shadowing.vert.glsl               |     1 +
 src/osgEarthUtil/SimpleOceanLayer                  |   146 +
 src/osgEarthUtil/SimpleOceanLayer.cpp              |   202 +
 src/osgEarthUtil/SimpleOceanLayer.frag.glsl        |    46 +
 src/osgEarthUtil/SimpleOceanLayer.vert.glsl        |    33 +
 src/osgEarthUtil/SimplePager                       |     4 +
 src/osgEarthUtil/SimplePager.cpp                   |    52 +-
 src/osgEarthUtil/Sky                               |    34 +-
 src/osgEarthUtil/Sky.cpp                           |    38 +-
 src/osgEarthUtil/SpatialData                       |     1 -
 src/osgEarthUtil/SpatialData.cpp                   |    51 +-
 src/osgEarthUtil/TFS                               |     2 +-
 src/osgEarthUtil/TFSPackager.cpp                   |    20 +-
 src/osgEarthUtil/TMS.cpp                           |    31 +-
 src/osgEarthUtil/TMSBackFiller.cpp                 |     5 +-
 src/osgEarthUtil/TMSPackager.cpp                   |   139 +-
 src/osgEarthUtil/TerrainProfile                    |     2 +-
 src/osgEarthUtil/TerrainProfile.cpp                |     2 +-
 src/osgEarthUtil/TileIndex.cpp                     |     9 +-
 src/osgEarthUtil/TileIndexBuilder.cpp              |     1 +
 src/osgEarthUtil/TopologyGraph                     |   147 +
 .../TopologyGraph.cpp}                             |   333 +-
 src/osgEarthUtil/UTMGraticule                      |   150 +-
 src/osgEarthUtil/UTMGraticule.cpp                  |   344 +-
 src/osgEarthUtil/UTMLabelingEngine                 |    79 +
 src/osgEarthUtil/UTMLabelingEngine.cpp             |   511 +
 src/osgEarthUtil/ViewFitter                        |    65 +
 src/osgEarthUtil/ViewFitter.cpp                    |   230 +
 src/osgEarthUtil/WMS.cpp                           |    16 +
 src/tests/CMakeLists.txt                           |    31 +
 src/tests/osgEarth_tests/CMakeLists.txt            |    17 +
 src/tests/osgEarth_tests/EndianTests.cpp           |    95 +
 src/tests/osgEarth_tests/FeatureTests.cpp          |    53 +
 src/tests/osgEarth_tests/GeoExtentTests.cpp        |   238 +
 src/tests/osgEarth_tests/ImageLayerTests.cpp       |    59 +
 src/tests/osgEarth_tests/SpatialReferenceTests.cpp |    92 +
 src/tests/osgEarth_tests/ThreadingTests.cpp        |   135 +
 .../AutoScale => tests/osgEarth_tests/main.cpp}    |    29 +-
 tests/aeqd.earth                                   |    22 +
 tests/annotation.earth                             |    12 +-
 tests/annotation_dateline.earth                    |    27 +
 tests/annotation_dateline_projected.earth          |    45 +
 tests/boston-gpu.earth                             |    28 +-
 tests/boston.earth                                 |    56 +-
 tests/boston_tfs.earth                             |    50 -
 tests/city_labels.xml                              |    25 +
 tests/clouds.earth                                 |    57 +-
 tests/datum_override.earth                         |     3 +-
 tests/{night.earth => day_night_mp.earth}          |     0
 tests/day_night_rex.earth                          |    61 +
 tests/detail_texture.earth                         |     7 +-
 tests/fade_elevation.earth                         |    59 -
 tests/feature_clip_plane.earth                     |    14 +-
 tests/feature_country_boundaries.earth             |    43 +-
 tests/feature_custom_filters.earth                 |    16 +-
 tests/feature_draped_lines.earth                   |    13 +-
 tests/feature_draped_polygons.earth                |    23 +-
 tests/feature_extrude.earth                        |    38 +-
 tests/feature_geom.earth                           |    28 +-
 tests/feature_gpx.earth                            |    11 +-
 tests/feature_inline_geometry.earth                |    18 +-
 tests/feature_labels.earth                         |    45 +-
 tests/feature_labels_script.earth                  |    26 +-
 tests/feature_levels_and_selectors.earth           |    10 +-
 tests/feature_model_scatter.earth                  |     4 +-
 tests/feature_models.earth                         |    11 +-
 tests/feature_occlusion_culling.earth              |    27 +-
 tests/feature_overlay.earth                        |    38 -
 tests/feature_population_cylinders.earth           |    60 +-
 tests/feature_raster.earth                         |    67 +-
 tests/feature_rasterize.earth                      |     4 -
 tests/feature_tfs.earth                            |    23 +-
 tests/feature_tfs_scripting.earth                  |    33 +-
 tests/feature_wfs.earth                            |    21 +-
 tests/fractal_elevation.earth                      |    99 +
 tests/gdal_multiple_files.earth                    |     4 +-
 tests/geomshader.earth                             |    20 +-
 tests/glsl.earth                                   |    30 +-
 tests/glsl_filter.earth                            |    16 -
 tests/graticule.earth                              |    36 -
 tests/graticules.earth                             |    79 +
 tests/intersect_filter.earth                       |     7 +-
 tests/land_cover_mixed.earth                       |    84 +
 tests/lod_blending.earth                           |    31 -
 tests/mapbox.earth                                 |   178 +
 tests/mask.earth                                   |     7 +-
 tests/mb_tiles.earth                               |    10 +-
 tests/min_max_range.earth                          |     4 +
 tests/multiple_heightfields.earth                  |     7 +-
 tests/nodata.earth                                 |     6 +-
 tests/noise.earth                                  |    77 -
 tests/normalmap.earth                              |    22 -
 tests/ocean.earth                                  |    31 +-
 tests/{ocean.earth => ocean_no_elevation.earth}    |    58 +-
 tests/openstreetmap_buildings.earth                |    50 +-
 tests/openstreetmap_flat.earth                     |     7 +-
 tests/openstreetmap_full.earth                     |    80 +-
 ...eathermap_clouds.earth => openweathermap.earth} |    16 +-
 tests/openweathermap_precipitation.earth           |    31 -
 tests/openweathermap_pressure.earth                |    31 -
 ...eadymap.earth => readymap-elevation-only.earth} |    10 +-
 tests/readymap-priority.earth                      |    35 -
 tests/readymap-rex.earth                           |     5 +-
 tests/readymap.earth                               |    10 +-
 ...adymap_pixel_size.earth => readymap_flat.earth} |     9 +-
 tests/roads-flattened.earth                        |   198 +
 tests/roads-test.earth                             |   107 +
 tests/roads.earth                                  |   109 +
 tests/scene_clamping.earth                         |   130 +
 tests/{gdal_tiff.earth => simple.earth}            |     7 +-
 tests/splat-blended-with-imagery.earth             |    47 +
 tests/{splat-edit.bat => splat-detail-tool.bat}    |     0
 tests/splat-gpunoise.bat                           |    31 -
 tests/splat-groundcover-tool.bat                   |    15 +
 tests/splat-ranges.earth                           |    55 -
 tests/splat-server.earth                           |   105 -
 tests/splat-with-mask-layer.earth                  |    92 +
 tests/splat-with-multiple-zones.earth              |   116 +
 tests/splat-with-rasterized-land-cover.earth       |    81 +
 tests/splat-with-vectors.earth                     |   109 -
 tests/splat.bat                                    |    14 -
 tests/splat.earth                                  |   132 +-
 tests/tess-coastlines.earth                        |   157 -
 tests/tess-masking.earth                           |   176 -
 tests/tess-terrain.earth                           |   146 -
 tests/tess_screen_space.earth                      |   120 -
 tests/test-morphing.earth                          |    50 -
 tests/triton.earth                                 |    31 +-
 tests/{readymap-osm.earth => utm.earth}            |    24 +-
 tests/viewpoints.xml                               |   131 +-
 tests/viewpoints_flat.xml                          |    74 +
 992 files changed, 56320 insertions(+), 38149 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index afdce64..6999b8c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,20 +17,20 @@ before_install:
 - sudo apt-get update -qq
 - sudo apt-get install -y cmake
 #- sudo apt-get install -y libopenscenegraph-dev
-- sudo apt-get install -y --force-yes openscenegraph=3.4.0
+- sudo apt-get install -y --force-yes openscenegraph=3.4.1
 - sudo apt-get install -y libgdal-dev
 - sudo apt-get install -y libgeos-dev
 - sudo apt-get install -y libsqlite3-dev
 - sudo apt-get install -y protobuf-compiler libprotobuf-dev
+- sudo apt-get install -y libpoco-dev
 # Debug print out osgversion
 - osgversion
 script:
-- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then cmake -DCMAKE_BUILD_TYPE=Release . && make -j3 ; fi
+- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then cmake -DCMAKE_BUILD_TYPE=Release . && make -j3 && sudo make install && sudo ldconfig; fi
+- if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then cd tests && osgEarth_tests ; fi
 notifications:
-  email:
-    recipients:
-    - jasonbeverage at pelicanmapping.com
-    - gwaldron at pelicanmapping.com
+  slack:
+    secure: E/9LpduB9vyavYKA4HgQxf48JB3cQmKOo0We829cox67aixQCKhYjHG0KmT6wnjVVKWRbHru0ho7Gi1NuVNdGXAOG7PMP7XFOJB7pBfczy7HmQhsAttX/dTltb0XzRFx9O5qk3q2Vnc7JXKO5HbFDWCYh0O3RFoph9XzTfAQBdBNsy/wyYD0fogYsWk43qQPI8lLukBfJ705sn2un+bkfgnuS9g/uuQmmqIhXwkAamAUaioryVHDaLz2e1bMKaq62Rl4yV+lbY1/hsg8Ob1o1jm5MnEjcHuVU1uugX34FZZFVWrF3OuEvfEmDv2tN2Q72Zz/xmJR37osZ8x55gRg5w+lal0VEo/U7leMABzu8/k4DXsPLGEY8ft3aalBbVw2qh/IMOc8Jyq4/wg6/2aet0mS9FAmWy59HnDyYTsZtUdiU4aRdVvQcgJs5JEXypMYlVCCv325ixaA6CxGplGAqST2XYu4U+QP [...]
 addons:
   coverity_scan:
     project:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0f0b284..a85db48 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-CMAKE_MINIMUM_REQUIRED(VERSION 2.6.4 FATAL_ERROR)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11 FATAL_ERROR)
 
 if(COMMAND cmake_policy)
     # Works around warnings libraries linked against that don't
@@ -9,18 +9,9 @@ if(COMMAND cmake_policy)
     # statements.
     cmake_policy(SET CMP0005 OLD)
     
-    
-    IF(COMMAND cmake_policy)
-            IF(${CMAKE_MAJOR_VERSION} GREATER 2)
-                # Qt5 qt5_use_modules usage was causing "Policy CMP0043 is not set: Ignore COMPILE_DEFINITIONS_<Config> properties." warnings
-                cmake_policy(SET CMP0043 NEW)
-            ENDIF()
-    ENDIF()
+    # Qt5 qt5_use_modules usage was causing "Policy CMP0043 is not set: Ignore COMPILE_DEFINITIONS_<Config> properties." warnings
+    cmake_policy(SET CMP0043 NEW)
 
-    # disable autolinking to qtmain as we have our own main() functions (new in Qt 5.1)
-    if(NOT "${CMAKE_VERSION}" VERSION_LESS 2.8.11)
-        cmake_policy(SET CMP0020 OLD)
-    endif(NOT "${CMAKE_VERSION}" VERSION_LESS 2.8.11)
 endif(COMMAND cmake_policy)
 
 #
@@ -32,7 +23,7 @@ SET_PROPERTY( GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "CMake Targets" )
 PROJECT(OSGEARTH)
 
 SET(OSGEARTH_MAJOR_VERSION 2)
-SET(OSGEARTH_MINOR_VERSION 8)
+SET(OSGEARTH_MINOR_VERSION 9)
 SET(OSGEARTH_PATCH_VERSION 0)
 SET(OSGEARTH_SOVERSION     0)
 
@@ -70,33 +61,29 @@ IF (OSGEARTH_EMBED_GIT_SHA)
   get_git_head_revision(GIT_REFSPEC OSGEARTH_GIT_SHA1)
 ENDIF (OSGEARTH_EMBED_GIT_SHA)
 
-# IPHONE_PORT at tom
-# Trying to get CMake to generate an XCode IPhone project, current efforts are to get iphoneos sdk 3.1 working
-# Added option which needs manually setting to select the IPhone SDK for building. We can only have one of the below 
-# set to true. Should realy have an OSG_BUILD_PLATFORM variable that we set to our desired platform
-OPTION(OSG_BUILD_PLATFORM_IPHONE "Enable IPhoneSDK Device support" OFF)
-OPTION(OSG_BUILD_PLATFORM_IPHONE_SIMULATOR "Enable IPhoneSDK Simulator support" OFF)
-IF(OSG_BUILD_PLATFORM_IPHONE OR OSG_BUILD_PLATFORM_IPHONE_SIMULATOR)
-  
-  #you need to manually set the default sdk version here
-  SET (IPHONE_SDKVER "6.1")
-  #the below is taken from ogre, it states the gcc stuff needs to happen before PROJECT() is called. I've no clue if we even need it
-  # Force gcc <= 4.2 on iPhone
-  include(CMakeForceCompiler)
-  CMAKE_FORCE_C_COMPILER(llvm-gcc-4.2 GNU)
-  CMAKE_FORCE_CXX_COMPILER(llvm-gcc-4.2 GNU)
-  SET(GCC_THUMB_SUPPORT NO)
-
-        #set either the device sdk or the simulator sdk. Can't find away to separate these in the same project
-        IF(OSG_BUILD_PLATFORM_IPHONE)
-            SET (IPHONE_DEVROOT "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer")
-            SET (IPHONE_SDKROOT "${IPHONE_DEVROOT}/SDKs/iPhoneOS${IPHONE_SDKVER}.sdk")
-        ELSE()
-            SET (IPHONE_DEVROOT "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer")
-            SET (IPHONE_SDKROOT "${IPHONE_DEVROOT}/SDKs/iPhoneSimulator${IPHONE_SDKVER}.sdk")
-        ENDIF()
+#
+SET(OSGEARTH_USE_GLES FALSE)
+
+# check for iOS build
+OPTION(OSGEARTH_BUILD_PLATFORM_IPHONE "Enable IPhoneSDK Device support" OFF)
+OPTION(OSGEARTH_BUILD_PLATFORM_IPHONE_SIMULATOR "Enable IPhoneSDK Simulator support" OFF)
+
+IF(OSGEARTH_BUILD_PLATFORM_IPHONE OR OSGEARTH_BUILD_PLATFORM_IPHONE_SIMULATOR)
+    SET(OSGEARTH_USE_GLES TRUE)
+    #you need to manually set the default sdk version here
+    SET (IPHONE_SDKVER "10.2" CACHE STRING "iOS SDK-Version")
+    SET (IPHONE_VERSION_MIN "8.0" CACHE STRING "IOS minimum os version, use 7.0 or greater to get 64bit support")
+    SET (IPHONE_PLATFORMSROOT "/Applications/Xcode.app/Contents/Developer/Platforms")
+
+    IF(OSGEARTH_BUILD_PLATFORM_IPHONE)
+        SET(CMAKE_OSX_SYSROOT "${IPHONE_PLATFORMSROOT}/iPhoneOS.platform/Developer/SDKs/iPhoneOS${IPHONE_SDKVER}.sdk" CACHE STRING "System root for iOS" FORCE)
+        SET(CMAKE_OSX_ARCHITECTURES "armv7;armv7s;arm64" CACHE STRING "Build architectures for iOS" FORCE)
+    ELSE()
+        SET(CMAKE_OSX_SYSROOT "${IPHONE_PLATFORMSROOT}/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator${IPHONE_SDKVER}.sdk" CACHE STRING "System root for iOS" FORCE)
+        SET(CMAKE_OSX_ARCHITECTURES "i386" CACHE STRING "Build architectures for iOS Simulator" FORCE)
+    ENDIF()
 ENDIF ()
-# IPHONE_PORT at tom
+
 
 # check if STLport is required
 OPTION(USE_STLPORT "Set to ON to build OSGEARTH with stlport instead of the default STL library." OFF)
@@ -122,7 +109,11 @@ IF(CMAKE_SYSTEM MATCHES IRIX)
     SET(CMAKE_THREAD_LIBS_INIT "" CACHE INTERNAL "")
 ENDIF(CMAKE_SYSTEM MATCHES IRIX)
 
-FIND_PACKAGE(OpenGL)
+IF (OSGEARTH_USE_GLES)
+    FIND_PACKAGE(OpenGLES) 
+ELSE ()
+    FIND_PACKAGE(OpenGL)
+ENDIF (OSGEARTH_USE_GLES)
 
 FIND_PACKAGE(CURL)
 FIND_PACKAGE(GDAL)
@@ -137,37 +128,13 @@ FIND_PACKAGE(RocksDB)
 FIND_PACKAGE(SilverLining QUIET)
 FIND_PACKAGE(Triton QUIET)
 
-FIND_PACKAGE(Protobuf QUIET)
-
-# JavaScript Engines:
-SET(V8_DIR "" CACHE PATH "set to base V8 install path")
-FIND_PACKAGE(V8)
+FIND_PACKAGE(PROTOBUF QUIET)
 
 SET (WITH_EXTERNAL_DUKTAPE FALSE CACHE BOOL "Use bundled or system wide version of Duktape")
 IF (WITH_EXTERNAL_DUKTAPE)
     FIND_PACKAGE(Duktape)
 ENDIF (WITH_EXTERNAL_DUKTAPE)
 
-OPTION(USE_V8 "Use V8 instead of Duktape if V8 is found" OFF)
-
-FIND_PACKAGE(Qt5Core QUIET)
-FIND_PACKAGE(Qt5Widgets QUIET)
-FIND_PACKAGE(Qt5Gui QUIET)
-FIND_PACKAGE(Qt5OpenGL QUIET)
-IF ( Qt5Core_FOUND AND Qt5Widgets_FOUND AND Qt5Gui_FOUND AND Qt5OpenGL_FOUND )
-    SET(QT_INCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS})
-ELSE()
-    FIND_PACKAGE(Qt4)
-    IF (QT4_FOUND)
-        INCLUDE(${QT_USE_FILE})
-		SET(QT_INCLUDES ${QT_INCLUDES} ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR}${QT_QTOPENGL_INCLUDE_DIR} )
-        SET(QT_ALL_LIBRARIES ${QT_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTOPENGL_LIBRARY})
-    ENDIF (QT4_FOUND)
-ENDIF ()
-
-OPTION(OSGEARTH_USE_QT "Enable to use Qt (build Qt-dependent libraries, plugins and examples)" OFF)
-OPTION(OSGEARTH_QT_BUILD_LEGACY_WIDGETS "Build the legacy Qt widgets" OFF)
-
 # option to install shaders:
 OPTION(OSGEARTH_INSTALL_SHADERS "Whether to deploy GLSL shaders when doing a Make INSTALL" OFF)
 
@@ -184,12 +151,57 @@ IF(UNIX)
     FIND_LIBRARY(MATH_LIBRARY m)
 ENDIF(UNIX)
 
+
+OPTION(OSGEARTH_QT_BUILD "Enable to use Qt (build Qt-dependent libraries, plugins and examples)" OFF)
+OPTION(OSGEARTH_QT_BUILD_LEGACY_WIDGETS "Build the legacy Qt widgets" OFF)
+set(OSGEARTH_QT_VERSION 5 CACHE STRING "Qt version to use")
+
+IF(OSGEARTH_QT_BUILD)
+  # To select a specific version of QT define DESIRED_QT_VERSION
+  # via cmake -DDESIRED_QT_VERSION=5
+  # QUIET option disables messages if the package cannot be found.
+  IF  (OSGEARTH_QT_VERSION)
+      IF  (OSGEARTH_QT_VERSION MATCHES "5")
+          FIND_PACKAGE(Qt5Widgets REQUIRED)
+      ELSEIF (OSGEARTH_QT_VERSION MATCHES "4")
+          FIND_PACKAGE(Qt4 REQUIRED)
+      ENDIF()
+  ELSE()
+      FIND_PACKAGE(Qt5Widgets QUIET)
+      IF ( NOT Qt5Widgets_FOUND )
+          FIND_PACKAGE(Qt4 REQUIRED)
+      ENDIF()
+  ENDIF()
+ENDIF(OSGEARTH_QT_BUILD)
+
+IF(Qt5Widgets_FOUND)
+  message(STATUS "Qt: Using version 5")
+  FIND_PACKAGE(Qt5Core QUIET)
+  FIND_PACKAGE(Qt5Gui QUIET)
+  FIND_PACKAGE(Qt5OpenGL QUIET)
+  FIND_PACKAGE(Qt5OpenGLExtensions QUIET)
+  IF ( Qt5Core_FOUND AND Qt5Widgets_FOUND AND Qt5Gui_FOUND AND Qt5OpenGL_FOUND AND Qt5OpenGLExtensions_FOUND )
+    SET(QT_INCLUDES ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5OpenGLExtensions_INCLUDE_DIRS})
+  ENDIF ()
+ELSEIF(QT4_FOUND)
+  message(STATUS "Qt: Using version 4")
+  INCLUDE(${QT_USE_FILE})
+  SET(QT_INCLUDES ${QT_INCLUDES} ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR}${QT_QTOPENGL_INCLUDE_DIR} )
+  SET(QT_ALL_LIBRARIES ${QT_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTXML_LIBRARY} ${QT_QTOPENGL_LIBRARY})
+ENDIF ()
+
+
 # Common global definitions
 #ADD_DEFINITIONS(-D)
 # Platform specific definitions
 
 
 IF(WIN32)
+  FIND_PACKAGE(GLCORE)
+  IF(GLCORE_FOUND)
+      INCLUDE_DIRECTORIES( ${GLCORE_INCLUDE_DIR} )
+  ENDIF()    
+
   IF(MSVC)
         # This option is to enable the /MP switch for Visual Studio 2005 and above compilers
         OPTION(WIN32_USE_MP "Set to ON to build osgEarth with the /MP option (Visual Studio 2005 and above)." OFF)
@@ -209,25 +221,6 @@ IF(WIN32)
     ENDIF(MSVC)
 ENDIF(WIN32)
 
-########################################################################################################
-##### these were settings located in SetupCommon.cmake used in Luigi builds.... find out what are useful
-########################################################################################################
-#luigi#SET(CMAKE_VERBOSE_MAKEFILE TRUE)
-#luigi#SET(CMAKE_SKIP_RPATH TRUE)
-#luigi#SET(CMAKE_SKIP_RULE_DEPENDENCY TRUE)
-#luigi#IF(UNIX)
-#luigi#    LIST_CONTAINS(contains "g++" ${CMAKE_CXX_COMPILER_LIST})
-#luigi#    IF (contains)
-#luigi#        MESSAGE(${MY_MESSAGE_DEFAULT} "${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} setting  CMAKE_CXX_COMPILER to g++")
-#luigi#        SET(CMAKE_CXX_COMPILER "g++")
-#luigi#        SET(CMAKE_CXX_COMPILER_LOADED 2)
-#luigi#        SET(CMAKE_CXX_COMPILER_WORKS 2)
-#luigi#    ENDIF (contains)
-#luigi#    SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
-#luigi#    SET(CMAKE_CXX_FLAGS_DEBUG "-ggdb -gstabs")
-#luigi#ENDIF(UNIX)
-########################################################################################################
-
 # Common to all platforms:
 
 SET(CMAKE_DEBUG_POSTFIX  "d" CACHE STRING "add a postfix, usually d on windows")
@@ -235,6 +228,14 @@ SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windo
 SET(CMAKE_RELWITHDEBINFO_POSTFIX "rd" CACHE STRING "add a postfix, usually empty on windows")
 SET(CMAKE_MINSIZEREL_POSTFIX "s" CACHE STRING "add a postfix, usually empty on windows")
 
+INCLUDE(OsgEarthMacroUtils)
+
+DETECT_OSG_VERSION()
+
+IF (NOT OPENSCENEGRAPH_VERSION)
+	SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION})
+ENDIF(NOT OPENSCENEGRAPH_VERSION)
+
 FIND_PACKAGE(OSG)
 
 # Make the headers visible to everything
@@ -280,11 +281,6 @@ SET(LIBRARY_OUTPUT_PATH ${OUTPUT_LIBDIR})
 LINK_DIRECTORIES( ${LINK_DIRECTORIES} ${OUTPUT_LIBDIR} )
 
 
-#SET(INSTALL_BINDIR VIRTUALPLANETBUILDER/bin)
-#SET(INSTALL_INCDIR VIRTUALPLANETBUILDER/include)
-#SET(INSTALL_LIBDIR VIRTUALPLANETBUILDER/lib)
-#SET(INSTALL_DOCDIR VIRTUALPLANETBUILDER/doc)
-
 ################################################################################
 # User Options
 
@@ -330,86 +326,44 @@ ENDIF(INSTALL_TO_OSG_DIR)
 # osgEarth Examples
 OPTION(BUILD_OSGEARTH_EXAMPLES "Enable to build osgEarth Examples" ON)
 
-INCLUDE(OsgEarthMacroUtils)
-
-DETECT_OSG_VERSION()
-
-SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION})
-
 # OE Core
 ADD_SUBDIRECTORY(src)
 
-# VPB Applications
-# OPTION(BUILD_APPLICATIONS "Enable to build OSGEARTH Applications" ON)
-# IF   (BUILD_APPLICATIONS)
-    # ADD_SUBDIRECTORY(applications)
-# ENDIF(BUILD_APPLICATIONS)
-
-
 
 # Set defaults for Universal Binaries. We want 32-bit Intel/PPC on 10.4
 # and 32/64-bit Intel/PPC on >= 10.5. Anything <= 10.3 doesn't support.
 IF(APPLE)
 
-        #Here we check if the user specified IPhone SDK
-    IF(OSG_BUILD_PLATFORM_IPHONE OR OSG_BUILD_PLATFORM_IPHONE_SIMULATOR)
-
-        IF(OSG_BUILD_PLATFORM_IPHONE)
-            SET(CMAKE_OSX_ARCHITECTURES "armv7" CACHE STRING "Build architectures for iOS" FORCE)
-            SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=4.1 -mno-thumb -arch armv7 -pipe -no-cpp-precomp" CACHE STRING "Flags used by the compiler during all build types." FORCE)
-        ELSE()
-            #simulator uses i386 architectures
-            SET(CMAKE_OSX_ARCHITECTURES "i386" CACHE STRING "Build architectures for iOS Simulator" FORCE)
-            SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-thumb -arch i386 -pipe -no-cpp-precomp" CACHE STRING "Flags used by the compiler during all build types." FORCE)
-        ENDIF()
-
-        #here we set the specific iphone sdk version. We can only set either device or simulator sdk. So if you want both you currently have to have two seperate projects
-        SET(CMAKE_OSX_SYSROOT "${IPHONE_SDKROOT}" CACHE STRING "System root for iOS" FORCE)
-
-        #hack, force link to opengles
-        set(CMAKE_EXE_LINKER_FLAGS "-framework Foundation -framework OpenGLES")
-
-        #use the IPhone windowing system
-        SET(OSG_WINDOWING_SYSTEM "IOS" CACHE STRING "Forced IPhone windowing system on iOS"  FORCE)
-        SET(OSG_DEFAULT_IMAGE_PLUGIN_FOR_OSX "imageio" CACHE STRING "Forced imageio default image plugin for iOS" FORCE)
-
-        #I think this or similar will be required for IPhone apps
-        #OPTION(OSG_BUILD_APPLICATION_BUNDLES "Enable the building of applications and examples as OSX Bundles" ON)
-
-    ELSE()
-
-        # These are just defaults/recommendations, but how we want to build
-        # out of the box. But the user needs to be able to change these options.
-        # So we must only set the values the first time CMake is run, or we
-        # will overwrite any changes the user sets.
-        # FORCE is used because the options are not reflected in the UI otherwise.
-        # Seems like a good place to add version specific compiler flags too.
-        IF(NOT OSGEARTH_CONFIG_HAS_BEEN_RUN_BEFORE)
-            # This is really fragile, but CMake doesn't provide the OS system
-            # version information we need. (Darwin versions can be changed
-            # independently of OS X versions.)
-            # It does look like CMake handles the CMAKE_OSX_SYSROOT automatically.
-            IF(EXISTS /Developer/SDKs/10.5.sdk)
-                SET(CMAKE_OSX_ARCHITECTURES "ppc;i386;ppc64;x86_64" CACHE STRING "Build architectures for OSX" FORCE)
-                SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.5 -ftree-vectorize -fvisibility-inlines-hidden" CACHE STRING "Flags used by the compiler during all build types." FORCE)
-            ELSE(EXISTS /Developer/SDKs/10.5.sdk)
-                IF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
-                    SET(CMAKE_OSX_ARCHITECTURES "ppc;i386" CACHE STRING "Build architectures for OSX" FORCE)
-                    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.4 -ftree-vectorize -fvisibility-inlines-hidden" CACHE STRING "Flags used by the compiler during all build types." FORCE)
-                ELSE(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
-                    # No Universal Binary support
-                    # Should break down further to set the -mmacosx-version-min,
-                    # but the SDK detection is too unreliable here.
-                ENDIF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
-            ENDIF(EXISTS /Developer/SDKs/10.5.sdk)
-        ENDIF(NOT OSGEARTH_CONFIG_HAS_BEEN_RUN_BEFORE)
-
-
-        OPTION(OSGEARTH_BUILD_APPLICATION_BUNDLES "Enable the building of applications and examples as OSX Bundles" OFF)
-        OPTION(OSGEARTH_BUILD_FRAMEWORKS "Compile frameworks instead of dylibs" OFF)
-        SET(OSGEARTH_BUILD_FRAMEWORKS_INSTALL_NAME_DIR "@executable_path/../Frameworks" CACHE STRING "Install name dir for compiled frameworks")
-
-    ENDIF()
+    # These are just defaults/recommendations, but how we want to build
+    # out of the box. But the user needs to be able to change these options.
+    # So we must only set the values the first time CMake is run, or we
+    # will overwrite any changes the user sets.
+    # FORCE is used because the options are not reflected in the UI otherwise.
+    # Seems like a good place to add version specific compiler flags too.
+    IF(NOT OSGEARTH_CONFIG_HAS_BEEN_RUN_BEFORE)
+        # This is really fragile, but CMake doesn't provide the OS system
+        # version information we need. (Darwin versions can be changed
+        # independently of OS X versions.)
+        # It does look like CMake handles the CMAKE_OSX_SYSROOT automatically.
+        IF(EXISTS /Developer/SDKs/10.5.sdk)
+            SET(CMAKE_OSX_ARCHITECTURES "ppc;i386;ppc64;x86_64" CACHE STRING "Build architectures for OSX" FORCE)
+            SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.5 -ftree-vectorize -fvisibility-inlines-hidden" CACHE STRING "Flags used by the compiler during all build types." FORCE)
+        ELSE(EXISTS /Developer/SDKs/10.5.sdk)
+            IF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
+                SET(CMAKE_OSX_ARCHITECTURES "ppc;i386" CACHE STRING "Build architectures for OSX" FORCE)
+                SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.4 -ftree-vectorize -fvisibility-inlines-hidden" CACHE STRING "Flags used by the compiler during all build types." FORCE)
+            ELSE(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
+                # No Universal Binary support
+                # Should break down further to set the -mmacosx-version-min,
+                # but the SDK detection is too unreliable here.
+            ENDIF(EXISTS /Developer/SDKs/MacOSX10.4u.sdk)
+        ENDIF(EXISTS /Developer/SDKs/10.5.sdk)
+    ENDIF(NOT OSGEARTH_CONFIG_HAS_BEEN_RUN_BEFORE)
+
+
+    OPTION(OSGEARTH_BUILD_APPLICATION_BUNDLES "Enable the building of applications and examples as OSX Bundles" OFF)
+    OPTION(OSGEARTH_BUILD_FRAMEWORKS "Compile frameworks instead of dylibs" OFF)
+    SET(OSGEARTH_BUILD_FRAMEWORKS_INSTALL_NAME_DIR "@executable_path/../Frameworks" CACHE STRING "Install name dir for compiled frameworks")
 
 ENDIF(APPLE)
 
diff --git a/CMakeModules/FindExpat.cmake b/CMakeModules/FindExpat.cmake
deleted file mode 100644
index 4e3dcd7..0000000
--- a/CMakeModules/FindExpat.cmake
+++ /dev/null
@@ -1,52 +0,0 @@
-# Locate EXPAT
-# This module defines
-# EXPAT_LIBRARY
-# EXPAT_FOUND, if false, do not try to link to expat
-# EXPAT_INCLUDE_DIR, where to find the headers
-#
-# $EXPAT_DIR is an environment variable that would
-# correspond to the ./configure --prefix=$EXPAT_DIR
-#
-# Created by Robert Osfield, adapted by GW
-
-FIND_PATH(EXPAT_INCLUDE_DIR expat.h
-    ${EXPAT_DIR}/include
-    $ENV{EXPAT_DIR}/include
-	$ENV{EXPAT_DIR}/Source/lib #Windows Binary Installer
-    $ENV{EXPAT_DIR}
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/include
-    /usr/include
-    /sw/include # Fink
-    /opt/local/include # DarwinPorts
-    /opt/csw/include # Blastwave
-    /opt/include
-    /usr/freeware/include
-    /devel
-)
-
-FIND_LIBRARY(EXPAT_LIBRARY
-    NAMES expat expat_i expat-2.0.1 libexpat
-    PATHS
-    ${EXPAT_DIR}/lib
-    $ENV{EXPAT_DIR}/lib
-	$ENV{EXPAT_DIR}/bin #Windows Binary Installer
-    $ENV{EXPAT_DIR}
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-SET(EXPAT_FOUND "NO")
-IF(EXPAT_LIBRARY AND EXPAT_INCLUDE_DIR)
-    SET(EXPAT_FOUND "YES")
-ENDIF(EXPAT_LIBRARY AND EXPAT_INCLUDE_DIR)
-
-
diff --git a/CMakeModules/FindGDAL.cmake b/CMakeModules/FindGDAL.cmake
index fdb9b25..f765c68 100644
--- a/CMakeModules/FindGDAL.cmake
+++ b/CMakeModules/FindGDAL.cmake
@@ -19,13 +19,17 @@
 # This makes the presumption that you are include gdal.h like
 # #include "gdal.h"
 
+SET(GDAL_DIR "" CACHE PATH "Root folder of GDAL dependency")
+
 FIND_PATH(GDAL_INCLUDE_DIR gdal.h
-  $ENV{GDAL_DIR}
-  NO_DEFAULT_PATH
-    PATH_SUFFIXES include
+    ${GDAL_DIR}
+    $ENV{GDAL_DIR}
+    NO_DEFAULT_PATH
+    PATH_SUFFIXES include include/gdal
 )
 
 FIND_PATH(GDAL_INCLUDE_DIR gdal.h
+   ${GDAL_DIR}
     PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this.
     NO_DEFAULT_PATH
     PATH_SUFFIXES include
@@ -33,6 +37,7 @@ FIND_PATH(GDAL_INCLUDE_DIR gdal.h
 
 FIND_PATH(GDAL_INCLUDE_DIR gdal.h
   PATHS
+  ${GDAL_DIR}/include
   ~/Library/Frameworks/gdal.framework/Headers
   /Library/Frameworks/gdal.framework/Headers
   /usr/local/include/gdal
@@ -59,7 +64,8 @@ FIND_PATH(GDAL_INCLUDE_DIR gdal.h
 FIND_LIBRARY(GDAL_LIBRARY 
   NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL 
   PATHS
-  c:/Program Files/FWTools2.1.0/lib
+  c:/Program Files/FWTools2.1.0/lib 
+  ${GDAL_DIR}/lib
   $ENV{GDAL_DIR}
   NO_DEFAULT_PATH
   PATH_SUFFIXES lib64 lib
@@ -67,13 +73,15 @@ FIND_LIBRARY(GDAL_LIBRARY
 FIND_LIBRARY(GDAL_LIBRARY 
   NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL 
   PATHS ${CMAKE_PREFIX_PATH} # Unofficial: We are proposing this.
-   c:/Program Files/FWTools2.1.0/lib
+    c:/Program Files/FWTools2.1.0/lib
+    ${GDAL_DIR}/lib
     NO_DEFAULT_PATH
     PATH_SUFFIXES lib64 lib
 )
 FIND_LIBRARY(GDAL_LIBRARY 
   NAMES gdal gdal_i gdal1.8.0 gdal1.7.0 gdal1.6.0 gdal1.5.0 gdal1.4.0 gdal1.3.2 GDAL 
   PATHS
+    ${GDAL_DIR}/lib
     ~/Library/Frameworks
     /Library/Frameworks
     /usr/local
diff --git a/CMakeModules/FindGEOS.cmake b/CMakeModules/FindGEOS.cmake
index 29054d1..444b005 100644
--- a/CMakeModules/FindGEOS.cmake
+++ b/CMakeModules/FindGEOS.cmake
@@ -5,7 +5,10 @@
 # GEOS_FOUND, if false, do not try to link to geos
 # GEOS_INCLUDE_DIR, where to find the headers
 
+SET(GEOS_DIR "" CACHE PATH "Root directory of GEOS distribution")
+
 FIND_PATH(GEOS_INCLUDE_DIR geos/geom/Geometry.h
+  ${GEOS_DIR}
   $ENV{GEOS_DIR}
   NO_DEFAULT_PATH
     PATH_SUFFIXES include
@@ -13,14 +16,15 @@ FIND_PATH(GEOS_INCLUDE_DIR geos/geom/Geometry.h
 
 FIND_PATH(GEOS_INCLUDE_DIR geos/geom/Geometry.h
   PATHS
-  ~/Library/Frameworks/geos/Headers
-  /Library/Frameworks/geos/Headers
+  ${GEOS_DIR}/include
   /usr/local/include/geos
   /usr/local/include/GEOS
   /usr/local/include
   /usr/include/geos
   /usr/include/GEOS
   /usr/include
+  ~/Library/Frameworks/geos/Headers
+  /Library/Frameworks/geos/Headers
   /sw/include/geos 
   /sw/include/GEOS 
   /sw/include # Fink
@@ -39,6 +43,7 @@ FIND_PATH(GEOS_INCLUDE_DIR geos/geom/Geometry.h
 FIND_LIBRARY(GEOS_LIBRARY
   NAMES geos
   PATHS
+    ${GEOS_DIR}/lib
     $ENV{GEOS_DIR}
     NO_DEFAULT_PATH
     PATH_SUFFIXES lib64 lib
@@ -59,17 +64,17 @@ FIND_LIBRARY(GEOS_LIBRARY
   PATH_SUFFIXES lib64 lib
 )
 
-
 FIND_LIBRARY(GEOS_LIBRARY_DEBUG
-  NAMES geod_d geos_i_d geosd
+  NAMES geos_d geos_i_d geosd
   PATHS
+    ${GEOS_DIR}/lib
     $ENV{GEOS_DIR}
     NO_DEFAULT_PATH
     PATH_SUFFIXES lib64 lib
 )
 
 FIND_LIBRARY(GEOS_LIBRARY_DEBUG
-  NAMES geod_d geos_i_d geosd
+  NAMES geos_d geos_i_d geosd
   PATHS
     ~/Library/Frameworks
     /Library/Frameworks
diff --git a/CMakeModules/FindGLCORE.cmake b/CMakeModules/FindGLCORE.cmake
new file mode 100644
index 0000000..18c0347
--- /dev/null
+++ b/CMakeModules/FindGLCORE.cmake
@@ -0,0 +1,36 @@
+# Finds the OpenGL Core Profile (cp) header file.
+# Looks for glcorearb.h
+# 
+# This script defines the following:
+#  GLCORE_FOUND // Set to TRUE if glcorearb.h is found
+#  GLCORE_INCLUDE_DIR // Parent directory of directory (gl, GL3, or OpenGL) containing the CP header.
+#  GLCORE_GLCOREARB_HEADER // advanced
+#
+# GLCORE_ROOT can be set as an environment variable or a CMake variable,
+# to the parent directory of the gl, GL3, or OpenGL directory containing the CP header.
+#
+
+
+FIND_PATH( GLCORE_GLCOREARB_HEADER
+    NAMES GL/glcorearb.h GL3/glcorearb.h OpenGL/glcorearb.h gl/glcorearb.h
+    HINTS ${GLCORE_ROOT}
+    PATHS ENV GLCORE_ROOT
+)
+
+set( GLCORE_INCLUDE_DIR )
+if( GLCORE_GLCOREARB_HEADER )
+    set( GLCORE_INCLUDE_DIR ${GLCORE_GLCOREARB_HEADER} )
+endif()
+
+
+# handle the QUIETLY and REQUIRED arguments and set
+# GLCORE_FOUND to TRUE as appropriate
+INCLUDE( FindPackageHandleStandardArgs )
+FIND_PACKAGE_HANDLE_STANDARD_ARGS( GLCORE
+    "Set GLCORE_ROOT as the parent of the directory containing the OpenGL core profile header."
+    GLCORE_INCLUDE_DIR )
+
+MARK_AS_ADVANCED(
+    GLCORE_INCLUDE_DIR
+    GLCORE_GLCOREARB_HEADER
+)
diff --git a/CMakeModules/FindJavaScriptCore.cmake b/CMakeModules/FindJavaScriptCore.cmake
deleted file mode 100644
index 3877cd5..0000000
--- a/CMakeModules/FindJavaScriptCore.cmake
+++ /dev/null
@@ -1,46 +0,0 @@
-# Locate JavaScriptCore
-# This module defines
-# JAVASCRIPTCORE_LIBRARY
-# JAVASCRIPTCORE_FOUND, if false, do not try to link to JavaScriptCore
-# JAVASCRIPTCORE_INCLUDE_DIR, where to find the headers
-
-FIND_PATH(JAVASCRIPTCORE_INCLUDE_DIR JavaScriptCore.h
-    ${JAVASCRIPTCORE_DIR}/include
-    $ENV{JAVASCRIPTCORE_DIR}/include
-    $ENV{JAVASCRIPTCORE_DIR}
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/include
-    /usr/include
-    /sw/include # Fink
-    /opt/local/include # DarwinPorts
-    /opt/csw/include # Blastwave
-    /opt/include
-    /usr/freeware/include
-    /devel
-)
-
-FIND_LIBRARY(JAVASCRIPTCORE_LIBRARY
-    NAMES libJavaScriptCore JavaScriptCore
-    PATHS
-    ${JAVASCRIPTCORE_DIR}
-    ${JAVASCRIPTCORE_DIR}/lib
-    $ENV{JAVASCRIPTCORE_DIR}
-    $ENV{JAVASCRIPTCORE_DIR}/lib
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-SET(JAVASCRIPTCORE_FOUND "NO")
-IF(JAVASCRIPTCORE_LIBRARY AND JAVASCRIPTCORE_INCLUDE_DIR)
-    SET(JAVASCRIPTCORE_FOUND "YES")
-ENDIF(JAVASCRIPTCORE_LIBRARY AND JAVASCRIPTCORE_INCLUDE_DIR)
-
-
diff --git a/CMakeModules/FindLevelDB.cmake b/CMakeModules/FindLevelDB.cmake
index 15e3db2..60e7743 100644
--- a/CMakeModules/FindLevelDB.cmake
+++ b/CMakeModules/FindLevelDB.cmake
@@ -4,8 +4,11 @@
 # LEVELDB_FOUND, if false, do not try to link to libnoise
 # LEVELDB_INCLUDE_DIR, where to find the headers
 
+SET(LEVELDB_DIR "" CACHE PATH "Root folder of LevelDB distribution")
+
 FIND_PATH(LEVELDB_INCLUDE_DIR leveldb/db.h
   PATHS
+  ${LEVELDB_DIR}
   $ENV{LEVELDB_DIR}
   NO_DEFAULT_PATH
     PATH_SUFFIXES include
@@ -38,6 +41,7 @@ FIND_PATH(LEVELDB_INCLUDE_DIR leveldb/db.h
 FIND_LIBRARY(LEVELDB_LIBRARY
   NAMES libleveldb leveldb leveldb_static
   PATHS
+    ${LEVELDB_DIR}
     $ENV{LEVELDB_DIR}
     NO_DEFAULT_PATH
     PATH_SUFFIXES lib64 lib
@@ -60,6 +64,7 @@ FIND_LIBRARY(LEVELDB_LIBRARY
 FIND_LIBRARY(LEVELDB_LIBRARY_DEBUG
   NAMES libleveldbd leveldbd leveldb_staticd
   PATHS
+    ${LEVELDB_DIR}
     $ENV{LEVELDB_DIR}
     NO_DEFAULT_PATH
     PATH_SUFFIXES lib64 lib
diff --git a/CMakeModules/FindLibNoise.cmake b/CMakeModules/FindLibNoise.cmake
deleted file mode 100644
index edbbc63..0000000
--- a/CMakeModules/FindLibNoise.cmake
+++ /dev/null
@@ -1,64 +0,0 @@
-# Locate libnoise.
-# This module defines
-# LIBNOISE_LIBRARY
-# LIBNOISE_FOUND, if false, do not try to link to libnoise
-# LIBNOISE_INCLUDE_DIR, where to find the headers
-
-FIND_PATH(LIBNOISE_INCLUDE_DIR noise.h
-  $ENV{LIBNOISE_DIR}
-  NO_DEFAULT_PATH
-    PATH_SUFFIXES include
-)
-
-FIND_PATH(LIBNOISE_INCLUDE_DIR noise.h
-  PATHS
-  ~/Library/Frameworks/noise/Headers
-  /Library/Frameworks/noise/Headers
-  /usr/local/include/noise
-  /usr/local/include/noise
-  /usr/local/include
-  /usr/include/noise
-  /usr/include/noise
-  /usr/include
-  /sw/include/noise 
-  /sw/include/noise 
-  /sw/include # Fink
-  /opt/local/include/noise
-  /opt/local/include/noise
-  /opt/local/include # DarwinPorts
-  /opt/csw/include/noise
-  /opt/csw/include/noise
-  /opt/csw/include # Blastwave
-  /opt/include/noise
-  /opt/include/noise
-  /opt/include  
-)
-
-FIND_LIBRARY(LIBNOISE_LIBRARY
-  NAMES libnoise noise
-  PATHS
-    $ENV{LIBNOISE_DIR}
-    NO_DEFAULT_PATH
-    PATH_SUFFIXES lib64 lib
-)
-
-FIND_LIBRARY(LIBNOISE_LIBRARY
-  NAMES libnoise noise
-  PATHS
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local
-    /usr
-    /sw
-    /opt/local
-    /opt/csw
-    /opt
-    /usr/freeware    
-  PATH_SUFFIXES lib64 lib
-)
-
-SET(LIBNOISE_FOUND "NO")
-IF(LIBNOISE_LIBRARY AND LIBNOISE_INCLUDE_DIR)
-  SET(LIBNOISE_FOUND "YES")
-ENDIF(LIBNOISE_LIBRARY AND LIBNOISE_INCLUDE_DIR)
-
diff --git a/CMakeModules/FindOSG.cmake b/CMakeModules/FindOSG.cmake
index 6b43069..d862452 100644
--- a/CMakeModules/FindOSG.cmake
+++ b/CMakeModules/FindOSG.cmake
@@ -119,8 +119,18 @@ FIND_OSG_LIBRARY( OSGMANIPULATOR_LIBRARY_DEBUG osgManipulatord )
 FIND_OSG_LIBRARY( OSGPARTICLE_LIBRARY osgParticle )
 FIND_OSG_LIBRARY( OSGPARTICLE_LIBRARY_DEBUG osgParticled )
 
-FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt )
-FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQtd )
+IF(OPENSCENEGRAPH_VERSION VERSION_LESS "3.5.6")
+  FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt )
+  FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQtd )
+ELSE(OPENSCENEGRAPH_VERSION VERSION_LESS "3.5.6")
+  IF(Qt5Widgets_FOUND)
+    FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt5 )
+    FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQt5d )
+  ELSE(Qt5Widgets_FOUND)
+    FIND_OSG_LIBRARY( OSGQT_LIBRARY osgQt )
+    FIND_OSG_LIBRARY( OSGQT_LIBRARY_DEBUG osgQtd )
+  ENDIF(Qt5Widgets_FOUND)
+ENDIF(OPENSCENEGRAPH_VERSION VERSION_LESS "3.5.6")
 
 FIND_OSG_LIBRARY( OPENTHREADS_LIBRARY OpenThreads )
 FIND_OSG_LIBRARY( OPENTHREADS_LIBRARY_DEBUG OpenThreadsd )
diff --git a/CMakeModules/FindOpenGLES.cmake b/CMakeModules/FindOpenGLES.cmake
new file mode 100644
index 0000000..be24eb4
--- /dev/null
+++ b/CMakeModules/FindOpenGLES.cmake
@@ -0,0 +1,12 @@
+IF(APPLE)
+    FIND_LIBRARY(OPENGL_LIBRARY NAMES OpenGLES
+        PATHS ${CMAKE_OSX_SYSROOT}/System/Library
+        PATH_SUFFIXES Frameworks
+        NO_DEFAULT_PATH)
+MESSAGE("Found OpenGLES library ${OPENGL_LIBRARY}") 
+    SET(OPENGL_LIBRARIES ${OPENGL_LIBRARY})
+    SET(OPENGL_INCLUDE_DIR "${OPENGL_LIBRARIES}/Headers")
+    SET(OPENGL_FOUND TRUE)
+ELSE ()
+    MESSAGE("Could not find OpenGLES library") 
+ENDIF()
diff --git a/CMakeModules/FindRocksDB.cmake b/CMakeModules/FindRocksDB.cmake
index 21d16a7..17fb335 100644
--- a/CMakeModules/FindRocksDB.cmake
+++ b/CMakeModules/FindRocksDB.cmake
@@ -1,5 +1,7 @@
 # Locate RocksDB
 
+SET(ROCKSDB_DIR "" CACHE PATH "Root directory of RocksDB distribution")
+
 FIND_PATH(ROCKSDB_INCLUDE_DIR rocksdb/db.h
   PATHS
   ${ROCKSDB_DIR}
diff --git a/CMakeModules/FindV8.cmake b/CMakeModules/FindV8.cmake
deleted file mode 100644
index dfb2d86..0000000
--- a/CMakeModules/FindV8.cmake
+++ /dev/null
@@ -1,245 +0,0 @@
-# Locate V8
-# This module defines
-# V8_LIBRARY
-# V8_FOUND, if false, do not try to link to V8
-# V8_INCLUDE_DIR, where to find the headers
-
-FIND_PATH(V8_INCLUDE_DIR v8.h
-    ${V8_DIR}/include
-    $ENV{V8_DIR}/include
-    $ENV{V8_DIR}
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/include
-    /usr/include
-    /sw/include # Fink
-    /opt/local/include # DarwinPorts
-    /opt/csw/include # Blastwave
-    /opt/include
-    /usr/freeware/include
-    /devel
-)
-
-# On non-Unix platforms (Mac and Windows specifically based on the forum),
-# V8 builds separate shared (or at least linkable) libraries for v8_base and v8_snapshot
-IF(NOT UNIX)
-    FIND_LIBRARY(V8_BASE_LIBRARY
-        NAMES v8_base v8_base.ia32 v8_base.x64 libv8_base
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Release/lib
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-
-    FIND_LIBRARY(V8_BASE_LIBRARY_DEBUG
-        NAMES v8_base v8_base.ia32 v8_base.x64 libv8_base
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Debug/lib
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-
-    FIND_LIBRARY(V8_SNAPSHOT_LIBRARY
-        NAMES v8_snapshot libv8_snapshot
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Release/lib
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-
-    FIND_LIBRARY(V8_SNAPSHOT_LIBRARY_DEBUG
-        NAMES v8_snapshot libv8_snapshot
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Debug/lib
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-
-# On Linux, there is just a libv8.so shared library built.
-# (well, there are pseudo-static libraries libv8_base.a and libv8_snapshot.a
-# but they don't seem to link correctly)
-ELSE()
-    FIND_LIBRARY(V8_LIBRARY
-        NAMES v8
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Release/lib
-        # Having both architectures listed is problematic if both have been
-        # built (which is the default)
-        ${V8_DIR}/out/ia32.release/lib.target/
-        ${V8_DIR}/out/x64.release/lib.target/
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-
-    FIND_LIBRARY(V8_LIBRARY_DEBUG
-        NAMES v8
-        PATHS
-        ${V8_DIR}
-        ${V8_DIR}/lib
-        ${V8_DIR}/build/Debug/lib
-        ${V8_DIR}/out/ia32.debug/lib.target/
-        ${V8_DIR}/out/x64.debug/lib.target/
-        $ENV{V8_DIR}
-        $ENV{V8_DIR}/lib
-        ~/Library/Frameworks
-        /Library/Frameworks
-        /usr/local/lib
-        /usr/lib
-        /sw/lib
-        /opt/local/lib
-        /opt/csw/lib
-        /opt/lib
-        /usr/freeware/lib64
-    )
-ENDIF(NOT UNIX)
-
-# icuuc and icui18n build fine on all platforms
-FIND_LIBRARY(V8_ICUUC_LIBRARY
-    NAMES icuuc libicuuc
-    PATHS
-    ${V8_DIR}
-    ${V8_DIR}/lib
-    ${V8_DIR}/build/Release/lib
-    ${V8_DIR}/out/ia32.release/lib.target/
-    ${V8_DIR}/out/x64.release/lib.target/
-    $ENV{V8_DIR}
-    $ENV{V8_DIR}/lib
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-FIND_LIBRARY(V8_ICUUC_LIBRARY_DEBUG
-    NAMES icuuc libicuuc
-    PATHS
-    ${V8_DIR}
-    ${V8_DIR}/lib
-    ${V8_DIR}/build/Debug/lib
-    ${V8_DIR}/out/ia32.debug/lib.target/
-    ${V8_DIR}/out/x64.debug/lib.target/
-    $ENV{V8_DIR}
-    $ENV{V8_DIR}/lib
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-FIND_LIBRARY(V8_ICUI18N_LIBRARY
-    NAMES icui18n libicui18n
-    PATHS
-    ${V8_DIR}
-    ${V8_DIR}/lib
-    ${V8_DIR}/build/Release/lib
-    ${V8_DIR}/out/ia32.release/lib.target/
-    ${V8_DIR}/out/x64.release/lib.target/
-    $ENV{V8_DIR}
-    $ENV{V8_DIR}/lib
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-FIND_LIBRARY(V8_ICUI18N_LIBRARY_DEBUG
-    NAMES icui18n libicui18n
-    PATHS
-    ${V8_DIR}
-    ${V8_DIR}/lib
-    ${V8_DIR}/build/Debug/lib
-    ${V8_DIR}/out/ia32.debug/lib.target/
-    ${V8_DIR}/out/x64.debug/lib.target/
-    $ENV{V8_DIR}
-    $ENV{V8_DIR}/lib
-    ~/Library/Frameworks
-    /Library/Frameworks
-    /usr/local/lib
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/csw/lib
-    /opt/lib
-    /usr/freeware/lib64
-)
-
-SET(V8_FOUND "NO")
-IF(NOT UNIX)
-    IF(V8_BASE_LIBRARY AND V8_SNAPSHOT_LIBRARY AND V8_ICUUC_LIBRARY AND V8_ICUI18N_LIBRARY AND V8_INCLUDE_DIR)
-        SET(V8_FOUND "YES")
-    ENDIF(V8_BASE_LIBRARY AND V8_SNAPSHOT_LIBRARY AND V8_ICUUC_LIBRARY AND V8_ICUI18N_LIBRARY AND V8_INCLUDE_DIR)
-ELSEIF(V8_LIBRARY AND V8_ICUUC_LIBRARY AND V8_ICUI18N_LIBRARY AND V8_INCLUDE_DIR)
-    SET(V8_FOUND "YES")
-ENDIF(NOT UNIX)
-
-
diff --git a/CMakeModules/ModuleInstall.cmake b/CMakeModules/ModuleInstall.cmake
index 0c510b4..4c13636 100644
--- a/CMakeModules/ModuleInstall.cmake
+++ b/CMakeModules/ModuleInstall.cmake
@@ -2,7 +2,15 @@
 # ${LIB_NAME}
 # ${LIB_PUBLIC_HEADERS}
 
-SET(INSTALL_INCDIR include)
+# Optional Vars:
+# ${HEADER_INSTALL_DIR}
+
+IF(HEADER_INSTALL_DIR)
+    SET(INSTALL_INCDIR include/${HEADER_INSTALL_DIR})
+ELSE()
+    SET(INSTALL_INCDIR include/${LIB_NAME})
+ENDIF()
+
 SET(INSTALL_BINDIR bin)
 IF(WIN32)
     SET(INSTALL_LIBDIR bin)
@@ -45,7 +53,7 @@ endif(OSGEARTH_INSTALL_SHADERS)
 IF(NOT OSGEARTH_BUILD_FRAMEWORKS)
     INSTALL(
         FILES        ${LIB_PUBLIC_HEADERS}
-        DESTINATION ${INSTALL_INCDIR}/${LIB_NAME}
+        DESTINATION ${INSTALL_INCDIR}
     )
 ELSE()
     SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
diff --git a/CMakeModules/OsgEarthMacroUtils.cmake b/CMakeModules/OsgEarthMacroUtils.cmake
index ccd5a69..e1bd5d4 100644
--- a/CMakeModules/OsgEarthMacroUtils.cmake
+++ b/CMakeModules/OsgEarthMacroUtils.cmake
@@ -3,62 +3,64 @@
 #######################################################################################################
 MACRO(DETECT_OSG_VERSION)
 
+    # Fall back to OSG_DIR if OSG_INCLUDE_DIR is not defined
+    if(OSG_DIR AND NOT OSG_INCLUDE_DIR AND EXISTS "${OSG_DIR}/include/osg/Version")
+        set(OSG_INCLUDE_DIR "${OSG_DIR}/include")
+    endif()
+
     OPTION(APPEND_OPENSCENEGRAPH_VERSION "Append the OSG version number to the osgPlugins directory" ON)
 	
-    # detect if osgversion can be found
-    FIND_PROGRAM(OSG_VERSION_EXE NAMES
-        osgversion
-        ${OSG_DIR}/bin/osgversion
-        ${OSG_DIR}/bin/osgversiond)
-        
-    IF(OSG_VERSION_EXE AND NOT OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION AND NOT OPENSCENEGRAPH_PATCH_VERSION)
-        #MESSAGE("OSGVERSION IS AT ${OSG_VERSION_EXE}")
-        # get parameters out of the osgversion
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} --major-number OUTPUT_VARIABLE OPENSCENEGRAPH_MAJOR_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} --minor-number OUTPUT_VARIABLE OPENSCENEGRAPH_MINOR_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} --patch-number OUTPUT_VARIABLE OPENSCENEGRAPH_PATCH_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} Matrix::value_type OUTPUT_VARIABLE OSG_USE_FLOAT_MATRIX OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} Plane::value_type OUTPUT_VARIABLE OSG_USE_FLOAT_PLANE OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} BoundingSphere::value_type OUTPUT_VARIABLE OSG_USE_FLOAT_BOUNDINGSPHERE OUTPUT_STRIP_TRAILING_WHITESPACE)
-        EXECUTE_PROCESS(COMMAND ${OSG_VERSION_EXE} BoundingBox::value_type OUTPUT_VARIABLE OSG_USE_FLOAT_BOUNDINGBOX OUTPUT_STRIP_TRAILING_WHITESPACE)
-
-        # setup version numbers if we have osgversion
-        SET(OPENSCENEGRAPH_MAJOR_VERSION "${OPENSCENEGRAPH_MAJOR_VERSION}" CACHE STRING "OpenSceneGraph major version number")
-        SET(OPENSCENEGRAPH_MINOR_VERSION "${OPENSCENEGRAPH_MINOR_VERSION}" CACHE STRING "OpenSceneGraph minor version number")
-        SET(OPENSCENEGRAPH_PATCH_VERSION "${OPENSCENEGRAPH_PATCH_VERSION}" CACHE STRING "OpenSceneGraph patch version number")
-        SET(OPENSCENEGRAPH_SOVERSION "${OPENSCENEGRAPH_SOVERSION}" CACHE STRING "OpenSceneGraph so version number")
-		
-        # just debug info
-        #MESSAGE(STATUS "Detected OpenSceneGraph v${OPENSCENEGRAPH_VERSION}.")
-
-        # setup float and double definitions
-        IF(OSG_USE_FLOAT_MATRIX MATCHES "float")
-            ADD_DEFINITIONS(-DOSG_USE_FLOAT_MATRIX)
-        ENDIF(OSG_USE_FLOAT_MATRIX MATCHES "float")
-        IF(OSG_USE_FLOAT_PLANE MATCHES "float")
-            ADD_DEFINITIONS(-DOSG_USE_FLOAT_PLANE)
-        ENDIF(OSG_USE_FLOAT_PLANE MATCHES "float")
-        IF(OSG_USE_FLOAT_BOUNDINGSPHERE MATCHES "double")
-            ADD_DEFINITIONS(-DOSG_USE_DOUBLE_BOUNDINGSPHERE)
-        ENDIF(OSG_USE_FLOAT_BOUNDINGSPHERE MATCHES "double")
-        IF(OSG_USE_FLOAT_BOUNDINGBOX MATCHES "double")
-            ADD_DEFINITIONS(-DOSG_USE_DOUBLE_BOUNDINGBOX)
-        ENDIF(OSG_USE_FLOAT_BOUNDINGBOX MATCHES "double")
-
-    ENDIF(OSG_VERSION_EXE AND NOT OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION AND NOT OPENSCENEGRAPH_PATCH_VERSION)
-	
-    #Initialize the version numbers to being empty.  If they were set by osgversion, they will be left alone
-	SET(OPENSCENEGRAPH_MAJOR_VERSION "" CACHE STRING "OpenSceneGraph major version number")
-    SET(OPENSCENEGRAPH_MINOR_VERSION "" CACHE STRING "OpenSceneGraph minor version number")
-    SET(OPENSCENEGRAPH_PATCH_VERSION "" CACHE STRING "OpenSceneGraph patch version number")
-    SET(OPENSCENEGRAPH_SOVERSION "" CACHE STRING "OpenSceneGraph so version number")
-	
-    if (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "")
-	  SET(OPENSCENEGRAPH_VERSION ${OPENSCENEGRAPH_MAJOR_VERSION}.${OPENSCENEGRAPH_MINOR_VERSION}.${OPENSCENEGRAPH_PATCH_VERSION})
-	else (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "")
-	  #MESSAGE("osgversion was found at ${OSG_VERSION_EXE} but failed to run")
-	  SET(OPENSCENEGRAPH_VERSION)
-	endif (OPENSCENEGRAPH_MAJOR_VERSION AND NOT OPENSCENEGRAPH_MINOR_VERSION STREQUAL "" AND NOT OPENSCENEGRAPH_PATCH_VERSION STREQUAL "")
+    # Try to ascertain the version...
+    # (Taken from CMake's FindOpenSceneGraph.cmake)
+    if(OSG_INCLUDE_DIR)
+        if(OpenSceneGraph_DEBUG)
+            message(STATUS "[ FindOpenSceneGraph.cmake:${CMAKE_CURRENT_LIST_LINE} ] "
+                "Detected OSG_INCLUDE_DIR = ${OSG_INCLUDE_DIR}")
+        endif()
+
+        set(_osg_Version_file "${OSG_INCLUDE_DIR}/osg/Version")
+        if("${OSG_INCLUDE_DIR}" MATCHES "\\.framework$" AND NOT EXISTS "${_osg_Version_file}")
+            set(_osg_Version_file "${OSG_INCLUDE_DIR}/Headers/Version")
+        endif()
+
+        if(EXISTS "${_osg_Version_file}")
+          file(STRINGS "${_osg_Version_file}" _osg_Version_contents
+               REGEX "#define (OSG_VERSION_[A-Z]+|OPENSCENEGRAPH_[A-Z]+_VERSION)[ \t]+[0-9]+")
+        else()
+          set(_osg_Version_contents "unknown")
+        endif()
+
+        string(REGEX MATCH ".*#define OSG_VERSION_MAJOR[ \t]+[0-9]+.*"
+            _osg_old_defines "${_osg_Version_contents}")
+        string(REGEX MATCH ".*#define OPENSCENEGRAPH_MAJOR_VERSION[ \t]+[0-9]+.*"
+            _osg_new_defines "${_osg_Version_contents}")
+        if(_osg_old_defines)
+            string(REGEX REPLACE ".*#define OSG_VERSION_MAJOR[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_MAJOR ${_osg_Version_contents})
+            string(REGEX REPLACE ".*#define OSG_VERSION_MINOR[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_MINOR ${_osg_Version_contents})
+            string(REGEX REPLACE ".*#define OSG_VERSION_PATCH[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_PATCH ${_osg_Version_contents})
+        elseif(_osg_new_defines)
+            string(REGEX REPLACE ".*#define OPENSCENEGRAPH_MAJOR_VERSION[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_MAJOR ${_osg_Version_contents})
+            string(REGEX REPLACE ".*#define OPENSCENEGRAPH_MINOR_VERSION[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_MINOR ${_osg_Version_contents})
+            string(REGEX REPLACE ".*#define OPENSCENEGRAPH_PATCH_VERSION[ \t]+([0-9]+).*"
+                "\\1" _osg_VERSION_PATCH ${_osg_Version_contents})
+        else()
+            message(WARNING "[ FindOpenSceneGraph.cmake:${CMAKE_CURRENT_LIST_LINE} ] "
+                "Failed to parse version number, please report this as a bug")
+        endif()
+        unset(_osg_Version_contents)
+
+        set(OPENSCENEGRAPH_VERSION "${_osg_VERSION_MAJOR}.${_osg_VERSION_MINOR}.${_osg_VERSION_PATCH}"
+                                    CACHE INTERNAL "The version of OSG which was detected")
+        if(OpenSceneGraph_DEBUG)
+            message(STATUS "[ FindOpenSceneGraph.cmake:${CMAKE_CURRENT_LIST_LINE} ] "
+                "Detected version ${OPENSCENEGRAPH_VERSION}")
+        endif()
+    endif()
 	
 	MARK_AS_ADVANCED(OPENSCENEGRAPH_VERSION)
 
@@ -88,38 +90,22 @@ ENDMACRO(DETECT_OSG_VERSION)
 #  the content of this library for linking when in debugging
 #######################################################################################################
 
-
 MACRO(LINK_WITH_VARIABLES TRGTNAME)
     FOREACH(varname ${ARGN})
         IF(${varname}_DEBUG)
-            IF(${varname})
+            IF(${varname}_RELEASE)
+                TARGET_LINK_LIBRARIES(${TRGTNAME} optimized "${${varname}_RELEASE}" debug "${${varname}_DEBUG}")
+            ELSE(${varname}_RELEASE)
                 TARGET_LINK_LIBRARIES(${TRGTNAME} optimized "${${varname}}" debug "${${varname}_DEBUG}")
-            ELSE(${varname})
-                TARGET_LINK_LIBRARIES(${TRGTNAME} debug "${${varname}_DEBUG}")
-            ENDIF(${varname})
+            ENDIF(${varname}_RELEASE)
         ELSE(${varname}_DEBUG)
-            TARGET_LINK_LIBRARIES(${TRGTNAME} "${${varname}}" )
+            TARGET_LINK_LIBRARIES(${TRGTNAME} ${${varname}} )
         ENDIF(${varname}_DEBUG)
     ENDFOREACH(varname)
 ENDMACRO(LINK_WITH_VARIABLES TRGTNAME)
 
 MACRO(LINK_INTERNAL TRGTNAME)
-    IF("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.4)
-        TARGET_LINK_LIBRARIES(${TRGTNAME} ${ARGN})
-    ELSE("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.4)
-        FOREACH(LINKLIB ${ARGN})
-            IF(MSVC AND OSG_MSVC_VERSIONED_DLL)
-                #when using versioned names, the .dll name differ from .lib name, there is a problem with that:
-                #CMake 2.4.7, at least seem to use PREFIX instead of IMPORT_PREFIX  for computing linkage info to use into projects,
-                # so we full path name to specify linkage, this prevent automatic inferencing of dependencies, so we add explicit depemdencies
-                #to library targets used
-                TARGET_LINK_LIBRARIES(${TRGTNAME} optimized "${OUTPUT_LIBDIR}/${LINKLIB}${CMAKE_RELEASE_POSTFIX}.lib" debug "${OUTPUT_LIBDIR}/${LINKLIB}${CMAKE_DEBUG_POSTFIX}.lib")
-                ADD_DEPENDENCIES(${TRGTNAME} ${LINKLIB})
-            ELSE(MSVC AND OSG_MSVC_VERSIONED_DLL)
-                TARGET_LINK_LIBRARIES(${TRGTNAME} optimized "${LINKLIB}${CMAKE_RELEASE_POSTFIX}" debug "${LINKLIB}${CMAKE_DEBUG_POSTFIX}")
-            ENDIF(MSVC AND OSG_MSVC_VERSIONED_DLL)
-        ENDFOREACH(LINKLIB)
-    ENDIF("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 2.4)
+    TARGET_LINK_LIBRARIES(${TRGTNAME} ${ARGN})
 ENDMACRO(LINK_INTERNAL TRGTNAME)
 
 MACRO(LINK_EXTERNAL TRGTNAME)
@@ -456,7 +442,7 @@ MACRO(SETUP_EXAMPLE EXAMPLE_NAME)
 
         SETUP_EXE(${IS_COMMANDLINE_APP})
 
-    INSTALL(TARGETS ${TARGET_TARGETNAME} RUNTIME DESTINATION share/OpenSceneGraph/bin  )
+    INSTALL(TARGETS ${TARGET_TARGETNAME} RUNTIME DESTINATION share/OpenSceneGraph/bin BUNDLE DESTINATION share/OpenSceneGraph/bin  )
 
 ENDMACRO(SETUP_EXAMPLE)
 
diff --git a/data/resources/textures_us/catalog.xml b/data/resources/textures_us/catalog.xml
index 3ee7f8b..b6912f8 100644
--- a/data/resources/textures_us/catalog.xml
+++ b/data/resources/textures_us/catalog.xml
@@ -379,6 +379,7 @@
        <image_width>25</image_width>
        <image_height>25</image_height>
        <tiled>true</tiled>
+       <atlas>false</atlas>
    </skin>
 
    <skin name="roof_tiled2" tags="rooftop">
@@ -386,6 +387,7 @@
        <image_width>25</image_width>
        <image_height>25</image_height>
        <tiled>true</tiled>
+       <atlas>false</atlas>
    </skin>
 
    <skin name="roof_tiled3" tags="rooftop">
@@ -393,6 +395,7 @@
        <image_width>25</image_width>
        <image_height>25</image_height>
        <tiled>true</tiled>
+       <atlas>false</atlas>
    </skin>
    
 <!-- rooftops : non-tiled -->
@@ -432,6 +435,7 @@
        <image_width>50</image_width>
        <image_height>50</image_height>
        <tiled>true</tiled>
+       <atlas>false</atlas>
    </skin>   
 
 </resources>
diff --git a/docs/source/about.rst b/docs/source/about.rst
index efecfc9..0bba018 100644
--- a/docs/source/about.rst
+++ b/docs/source/about.rst
@@ -34,15 +34,16 @@ Since osgEarth_ is a free open source SDK, the source code is available to
 anyone and we welcome and encourage community participation when it comes
 to testing, adding features, and fixing bugs.
 
-**Support Forum**
+**Public Forum**
 
-    The best way to interact with the osgEarth team and the user community is
+    The first way to interact with the osgEarth team and the user community is
     through the `support forum`_. **Please read** and follow these guidelines for
-    using the forum:
+    using the forum. FOLLOWING THESE GUIDELINES will make it MUCH MORE LIKELY
+    that someone will respond and try to help:
 
     * Sign up for an account and use your real name. You can participate
-      anonymously, but using your real name helps build a stronger community
-      (and makes it more likely that we will get to your question sooner).
+      anonymously, but using your real name helps build a stronger community.
+      Sign your posts too!
       
     * Limit yourself to *one topic* per post. Asking multiple questions in one
       post makes it too hard to keep track of responses.
@@ -54,10 +55,17 @@ to testing, adding features, and fixing bugs.
       
     * Be patient!
 
+**Priority Support**
+
+    If you have several questions, or need more in-depth help involving code
+    review, design, etc., consider purchasing `Priority Support`_ directly
+    from Pelican Mapping (the maintainers of osgEarth). Priority Support
+    gives you tracked, timely, personal email-based assistance!
+
 **OSG Forum**
 
     Since osgEarth_ is built on top of OpenSceneGraph_, many questions we get
-    on the message boards are *really* OSG questions. We will still try our
+    on the message boards are really OSG questions. We will still try our
     best to help. But it's worth your while to join the `OSG Mailing List`_ or
     read the `OSG Forum`_ regularly as well.
     
@@ -109,16 +117,17 @@ Maintainers
 `Pelican Mapping`_ maintains osgEarth_.
 
 
-.. _osgEarth:        http://osgEarth.org
-.. _OpenSceneGraph:  http://openscenegraph.org
-.. _Pelican Mapping: http://pelicanmapping.com
-.. _LGPL:            http://www.gnu.org/copyleft/lesser.html
-.. _Glenn:           http://twitter.com/#!/glennwaldron
-.. _Jason:           http://twitter.com/#!/jasonbeverage
-.. _Jeff:            http://twitter.com/#!/_jeffsmith
-.. _Paul:            http://twitter.com/#!/p_levy
-.. _ at pelicanmapping: https://twitter.com/pelicanmapping
-.. _Google+ Page:    https://plus.google.com/b/104014917856468748129/104014917856468748129/posts
+.. _osgEarth:         http://osgEarth.org
+.. _OpenSceneGraph:   http://openscenegraph.org
+.. _Pelican Mapping:  http://pelicanmapping.com
+.. _Priority Support: http://web.pelicanmapping.com/priority-support
+.. _LGPL:             http://www.gnu.org/copyleft/lesser.html
+.. _Glenn:            http://twitter.com/#!/glennwaldron
+.. _Jason:            http://twitter.com/#!/jasonbeverage
+.. _Jeff:             http://twitter.com/#!/_jeffsmith
+.. _Paul:             http://twitter.com/#!/p_levy
+.. _ at pelicanmapping:  https://twitter.com/pelicanmapping
+.. _Google+ Page:     https://plus.google.com/b/104014917856468748129/104014917856468748129/posts
 
 .. _support forum:    http://forum.osgearth.osg
 .. _OSG Mailing List: http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
diff --git a/docs/source/data.rst b/docs/source/data.rst
index 2ebb23b..de13c46 100644
--- a/docs/source/data.rst
+++ b/docs/source/data.rst
@@ -13,13 +13,8 @@ Help us add useful sources of Free data to this list.
     * `USGS National Map`_ - Elevation, orthoimagery, hydrography, geographic names, boundaries,
       transportation, structures, and land cover products for the US.
     
-    * `NASA EOSDIS`_ - NASA's Global Imagery Browse Services (GIBS) replaces the agency's old
-      JPL OnEarth site for global imagery products like MODIS.
-       
     * `NASA BlueMarble`_ - NASA's whole-earth imagery (including topography and bathymetry maps)
     
-    * `NRL GIDB`_ - US Naval Research Lab's GIDB OpenGIS Web Services
-    
     * `Natural Earth`_ - Free vector and raster map data at various scales
     
     * `Virtual Terrain Project`_ - Various sources for whole-earth imagery
@@ -41,10 +36,10 @@ Help us add useful sources of Free data to this list.
 
     * `OpenStreetMap`_ - Worldwide, community-sources street and land use data (vectors and rasterized tiles)
     
-    * `DIVA-GIS`_ - Free low-resolution vector data for any country
-    
     * `Natural Earth`_ - Free vector and raster map data at various scales
     
+    * `DIVA-GIS`_ - Free low-resolution vector data for any country
+    
 
 .. _CGIAR:                      http://srtm.csi.cgiar.org/
 .. _CGIAR European mirror:      ftp://xftp.jrc.it/pub/srtmV4/
@@ -52,15 +47,13 @@ Help us add useful sources of Free data to this list.
 .. _GEBCO:                      http://www.gebco.net/
 .. _GLCF:                       http://glcf.umiacs.umd.edu/data/srtm/
 .. _OpenStreetMap:              http://openstreetmap.org
-.. _NASA EOSDIS:                http://earthdata.nasa.gov/about-eosdis/system-description/global-imagery-browse-services-gibs
 .. _NASA BlueMarble:            http://visibleearth.nasa.gov/view_cat.php?categoryID=1484
 .. _Natural Earth:              http://www.naturalearthdata.com/
-.. _NRL GIDB:                   http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet
 .. _SRTM30+:                    ftp://topex.ucsd.edu/pub/srtm30_plus/
 .. _USGS National Map:          http://nationalmap.gov/viewer.html
 .. _Virtual Terrain Project:    http://vterrain.org/Imagery/WholeEarth/
 .. _Bing Maps:                  http://www.microsoft.com/maps/choose-your-bing-maps-API.aspx
-.. _ReadyMap.org:               http://readymap.org/index_orig.html
+.. _ReadyMap.org:               http://readymap.org
 
 ----
 
@@ -88,30 +81,64 @@ Tips for Preparing your own Data
     **Build internal tiles**
     
     Typically formats such as GeoTiff store their pixel data in scanlines.
-    This generally works well, but because of the tiled approach that osgEarth
-    uses to access the data, you may find that using a tiled dataset will be more
-    efficient as osgEarth doens't need to read nearly as much data from disk to
-    extract a tile.
+    However, using a tiled dataset will be more efficient for osgEarth because
+    of how it uses tiles internally.
     
     To create a tiled GeoTiff using gdal_translate, issue the following command::
     
-        gdal_translate -of GTiff -co "TILED=YES" myfile.tif myfile_tiled.tif
+        gdal_translate -of GTiff -co TILED=YES input.tif output.tif
+        
+    Take is a step further and use compression to save space. You can use internal
+    JPEG compression if your data contains no transparency::
+    
+        gdal_translate -of GTiff -co TILED=YES -co COMPRESS=JPG input.tif output.tif   
+    
 
     **Build overviews**
     
     Adding overviews (also called ''pyramids'' or ''rsets'') can sometimes increase
-    the performance of a datasource in osgEarth.  You can use the
-    `gdaladdo <http://gdal.org/gdaladdo.html>`_ utility to add overviews to a dataset.
+    the performance of a large data source in osgEarth.  You can use the
+    `gdaladdo <http://gdal.org/gdaladdo.html>`_ utility to add overviews to a dataset::
     
-    For example::
-
         gdaladdo -r average myimage.tif 2 4 8 16
 
+
+**Building tile sets with osgearth_conv**
+
+   Pre-tiling your imagery can speed up load time dramatically, especially over the network.   
+   In fact, if you want to serve your data over the network, this is the only way!
+
+   *osgearth_conv* is a low-level conversion tool that comes with osgEarth. One useful 
+   application of the tool is tile up a large GeoTIFF (or other input) in a tiled format.   
+   Note: this approach only works with drivers that support writing (MBTiles, TMS).
+
+   To make a portable MBTiles file::
+
+       osgearth_conv --in driver gdal --in url myLargeFile.tif
+                     --out driver mbtiles --out filename myData.mbtiles
+                     --out format jpg
+
+   If you want to serve tiles from a web server, use TMS::
+
+       osgearth_conv --in driver gdal --in url myLargeData.tif
+                     --out driver tms --out url myLargeData/tms.xml
+                     --out format jpg
+
+   That will yield a folder (called "myLargeData" in this case) that you can deploy on the web
+   behind any standard web server (e.g. Apache).
+   
+   **Tip:** If you are tiling elevation data, you will need to add the ``--elevation`` option.
+   
+   **Tip:** The ``jpg`` format does NOT support transparency. If your data was an alpha
+   channel, use ``png`` instead.
+   
+   Just type *osgearth_conv* for a full list of options. The ``--in`` and ``--out`` options
+   correspond directly to properties you would normally include in an Earth file.
+   
         
-**Building tile sets**
+**Building tile sets with the packager**
 
-    Another way to speed up imagery and elevation loading in osgEarth is to build **tile sets**.
-    In fact, if you want to serve your data over the network, this is the only way!
+    Another way to speed up imagery and elevation loading in osgEarth is to build tile sets.
     
     This process takes the source data and chops it up into a quad-tree hierarchy of discrete
     *tiles* that osgEarth can load very quickly. Normally, if you load a GeoTIFF (for example),
@@ -140,3 +167,13 @@ Tips for Preparing your own Data
         --keep-empties                      Writes fully transparent image tiles (normally discarded)
         --db-options                        An optional OSG options string
         --verbose                           Displays progress of the operation
+        
+**Spatial indexing for feature data**
+
+    Large vector feature datasets (e.g., shapefiles) will benefit greatly from a spatial index.
+    Using the *ogrinfo* tool (included with GDAL/OGR binary distributions) you can create a 
+    spatial index for your vector data like so::
+
+        ogrinfo -sql "CREATE SPATIAL INDEX ON myfile" myfile.shp
+
+    For shapefiles, this will generate a ".qix" file that contains the spatial index information.
diff --git a/docs/source/developer/shader_composition.rst b/docs/source/developer/shader_composition.rst
index 240ed30..9dcfbf6 100644
--- a/docs/source/developer/shader_composition.rst
+++ b/docs/source/developer/shader_composition.rst
@@ -119,14 +119,14 @@ into various locations in the shader pipeline.
 For example, let's use user functions to create a simple "haze" effect::
 
     // haze_vertex:
-    varying vec3 v_pos;
+    out vec3 v_pos;
     void setup_haze(inout vec4 vertexView)
     {
         v_pos = vertexView.xyz;
     }
     
     // haze_fragment:
-    varying vec3 v_pos;
+    in vec3 v_pos;
     void apply_haze(inout vec4 color)
     {
         float dist = clamp( length(v_pos)/10000000.0, 0, 0.75 );
diff --git a/docs/source/faq.rst b/docs/source/faq.rst
index 1d1d777..45d730c 100644
--- a/docs/source/faq.rst
+++ b/docs/source/faq.rst
@@ -143,7 +143,7 @@ What is the best practice for using GitHub?
 	
 	1. Create your own GitHub account and log in.
 	2. Clone the osgEarth repo.
-	3. Work from your clone. Sync it to the main repository peridocially to get the
+	3. Work from your clone. Sync it to the main repository periodically to get the
 	   latest changes.
 
 
@@ -176,7 +176,11 @@ Can I hire someone to help me with osgEarth?
     services. The easiest way to get in touch with us is through our web site
     `contact form`_.
     
-.. _contact form:   http://pelicanmapping.com/?page_id=2
+    Pelican also offers a `Priority Support`_ package that is a good fit for 
+    companies that prefer to do most of their development in-house.
+    
+.. _contact form:     http://pelicanmapping.com/?page_id=2
+.. _Priority Support: http://web.pelicanmapping.com/priority-support/
 
 
 ----
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 8a50310..806bd14 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -26,6 +26,7 @@ Table of Contents
    references/index
    faq
    releasenotes
+   support
 
 
 .. _osgEarth: http://osgearth.org
diff --git a/docs/source/references/drivers/tile/tms.rst b/docs/source/references/drivers/tile/tms.rst
index 66eabb1..9d53e1f 100644
--- a/docs/source/references/drivers/tile/tms.rst
+++ b/docs/source/references/drivers/tile/tms.rst
@@ -11,9 +11,9 @@ Example usage::
     
 Properties:
 
-    :url:      Root URL (or pathname) of the TMS repository
-    :tmsType:  Set to ``google`` to invert the Y axis of the tile index
-    :format:   Override the format reported by the service (e.g., jpg, png)
+    :url:       Root URL (or pathname) of the TMS repository
+    :tms_type:  Set to ``google`` to invert the Y axis of the tile index
+    :format:    Override the format reported by the service (e.g., jpg, png)
 
 
 .. _Tile Map Service:  http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
diff --git a/docs/source/references/earthfile.rst b/docs/source/references/earthfile.rst
index 939b740..7333a01 100644
--- a/docs/source/references/earthfile.rst
+++ b/docs/source/references/earthfile.rst
@@ -16,7 +16,7 @@ The *map* is the top-level element in an earth file.
         <:ref:`elevation <ElevationLayer>`>
         <:ref:`model     <ModelLayer>`>
         <:ref:`mask      <MaskLayer>`>
-        
+        <:ref:`libraries <Libraries>`>
 
 +------------------------+--------------------------------------------------------------------+
 | Property               | Description                                                        |
@@ -520,3 +520,19 @@ color data in a layer before the osgEarth engine composites it into the terrain.
             
 You can chain multiple color filters together. Please refer to :doc:`/references/colorfilters` for
 details on color filters.
+
+.. _Libraries:
+
+Libraries
+~~~~~~~~~
+Preload any libraries.
+
+.. parsed-literal::
+
+    <libraries>a</libraries>
+
+Multiple library names could be listed by using ';' as separator. 
+    
+    <libraries>a;b;c;d;e</libraries>
+
+The libraries are searched in the osg library path and library name needs to follow the osg nodekit library name convention (postfixed with osg library version)
diff --git a/docs/source/references/envvars.rst b/docs/source/references/envvars.rst
index 8623fb5..80ae5ab 100644
--- a/docs/source/references/envvars.rst
+++ b/docs/source/references/envvars.rst
@@ -44,7 +44,7 @@ Networking:
     :OSGEARTH_HTTP_TIMEOUT:                Sets an HTTP timeout (seconds)
     :OSG_CURL_PROXY:                       Sets a proxy server for HTTP requests (string)
     :OSG_CURL_PROXYPORT:                   Sets a proxy port for HTTP proxy server (integer)
-    :OSGEARTH_PROXYAUTH:                   Sets proxy authentication information (username:password)
+    :OSGEARTH_CURL_PROXYAUTH:              Sets proxy authentication information (username:password)
     :OSGEARTH_SIMULATE_HTTP_RESPONSE_CODE: Simulates HTTP errors (for debugging; set to HTTP response code)
 
 Misc:
diff --git a/docs/source/startup.rst b/docs/source/startup.rst
index 0062926..d3ae246 100644
--- a/docs/source/startup.rst
+++ b/docs/source/startup.rst
@@ -95,7 +95,7 @@ Here are a few tips.
 .. _GDAL:           http://www.gdal.org/
 .. _GDAL binaries:  http://www.gisinternals.com/
 .. _FWTools:        http://fwtools.maptools.org/
-.. _AlphaPixel:     http://openscenegraph.alphapixel.com/osg/downloads/openscenegraph-third-party-library-downloads
+.. _AlphaPixel:     http://downloads.alphapixel.org/
 .. _Mike Weiblen:   http://mew.cx/osg/
 .. _the forum:      http://forum.osgearth.org
 .. _LevelDB:        https://github.com/pelicanmapping/leveldb
diff --git a/docs/source/support.rst b/docs/source/support.rst
new file mode 100644
index 0000000..b4270b8
--- /dev/null
+++ b/docs/source/support.rst
@@ -0,0 +1,24 @@
+osgEarth Priority Support
+=========================
+
+The osgEarth free open source SDK is a leading platform for mapping and visualization. But let’s be honest, there’s a lot of learning involved in crafting a geospatial-enabled application! Whether you are using osgEarth or other geospatial platforms, we’re here to help.
+
+`Priority Support`_ is the best way to get peace of mind as you develop your own geospatial applications. Here’s what you can expect:
+
+    * Private, e-mail based support tickets, tracked in our system
+    * Quick turnaround times
+    * Custom code examples
+    * Code analysis and recommendations
+    * Testing and evaluation to help you track down problems
+    * Bug fixes to our open source software
+    * Recommendations on best practices
+    * General advice on anything OSG or geospatial!
+
+Go to the `Priority Support`_ page on our web site for pricing and terms. 
+
+How can we help you?
+
+----
+*Copyright Pelican Mapping Inc.*
+
+.. _Priority Support: http://web.pelicanmapping.com/priority-support/
diff --git a/docs/source/user/features.rst b/docs/source/user/features.rst
index a26b8be..d619f94 100644
--- a/docs/source/user/features.rst
+++ b/docs/source/user/features.rst
@@ -31,7 +31,7 @@ and then use that image tile in a normal image layer.
 osgEarth has one rasterizing feature driver: the ``agglite`` driver. Here's an example
 that renders an ESRI Shapefile as a rasterized image layer::
 
-    <model name="my layer" driver="agglite">
+    <image name="my layer" driver="agglite">
         <features name="states" driver="ogr">
             <url>states.shp</url>
         </features>
@@ -43,7 +43,7 @@ that renders an ESRI Shapefile as a rasterized image layer::
                 }
             </style>
         </styles>
-    </model>
+    </image>
 
 Tessellation
 ~~~~~~~~~~~~
@@ -440,4 +440,4 @@ Layout Settings
                         Default = 1.0.
     :min_expiry_time:   Minimum time, in second, before a feature tile is eligible for pageout.
                         Set this to a negative number to disable expiration altogether (i.e., tiles
-                        will never page out).
\ No newline at end of file
+                        will never page out).
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d89c344..7035c3f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -7,7 +7,7 @@ FOREACH( lib
          osgEarthUtil )
 
     ADD_SUBDIRECTORY(${lib})
-    
+
     SET_PROPERTY(TARGET ${lib} PROPERTY FOLDER "Core")
 
 ENDFOREACH( lib )
@@ -18,29 +18,19 @@ FOREACH( lib
          osgEarthSplat
          osgEarthSilverLining
          osgEarthTriton )
-    add_subdirectory( ${lib} )    
+    add_subdirectory( ${lib} )
 ENDFOREACH( lib )
 
 ADD_SUBDIRECTORY( osgEarthDrivers )
 
-IF(NOT OSG_BUILD_PLATFORM_IPHONE AND NOT OSG_BUILD_PLATFORM_IPHONE_SIMULATOR AND NOT ANDROID)
+IF(NOT ANDROID)
     ADD_SUBDIRECTORY( applications )
 ENDIF()
 
-#IF (Qt5Widgets_FOUND OR QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
-#    ADD_SUBDIRECTORY(osgEarthQt)
-#    SET_PROPERTY(TARGET osgEarthQt PROPERTY FOLDER "Libs")
-#ENDIF()
-
-#IF (SILVERLINING_FOUND)
-#    ADD_SUBDIRECTORY(osgEarthSilverLining)
-    #SET_PROPERTY(TARGET osgEarthSilverLining PROPERTY FOLDER "Extensions")
-#ENDIF()
+IF(NOT OSGEARTH_BUILD_PLATFORM_IPHONE AND NOT OSGEARTH_BUILD_PLATFORM_IPHONE_SIMULATOR)
+    ADD_SUBDIRECTORY( tests )
+ENDIF()
 
-#IF (TRITON_FOUND)
-    #ADD_SUBDIRECTORY(osgEarthTriton)
-    #SET_PROPERTY(TARGET osgEarthTriton PROPERTY FOLDER "Extensions")
-#ENDIF()
 
 IF(MSVC80)
   OPTION(OSGEARTH_MSVC_GENERATE_PLUGINS_AND_WRAPPERS_MANIFESTS "Generate or not manifests files under VS8 for dynamically loaded dlls" ON)
diff --git a/src/applications/CMakeLists.txt b/src/applications/CMakeLists.txt
index 2b5d1e4..3b5b28f 100644
--- a/src/applications/CMakeLists.txt
+++ b/src/applications/CMakeLists.txt
@@ -28,12 +28,14 @@ SET(TARGET_DEFAULT_APPLICATION_FOLDER "Applications")
 
 SET(TARGET_DEFAULT_LABEL_PREFIX "Tool")
 SET(TARGET_DEFAULT_APPLICATION_FOLDER "Tools")
+
+IF(NOT OSGEARTH_BUILD_PLATFORM_IPHONE AND NOT OSGEARTH_BUILD_PLATFORM_IPHONE_SIMULATOR)
+
 ADD_SUBDIRECTORY(osgearth_viewer)
 ADD_SUBDIRECTORY(osgearth_seed)
 ADD_SUBDIRECTORY(osgearth_package)
 ADD_SUBDIRECTORY(osgearth_tfs)
 ADD_SUBDIRECTORY(osgearth_boundarygen)
-ADD_SUBDIRECTORY(osgearth_backfill)
 ADD_SUBDIRECTORY(osgearth_overlayviewer)
 ADD_SUBDIRECTORY(osgearth_version)
 ADD_SUBDIRECTORY(osgearth_tileindex)
@@ -41,17 +43,15 @@ ADD_SUBDIRECTORY(osgearth_atlas)
 ADD_SUBDIRECTORY(osgearth_conv)
 ADD_SUBDIRECTORY(osgearth_3pv)
 
-IF (Qt5Widgets_FOUND OR QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT AND OSGEARTH_QT_BUILD_LEGACY_WIDGETS)
+IF (Qt5Widgets_FOUND OR QT4_FOUND AND NOT ANDROID AND OSGEARTH_QT_BUILD AND OSGEARTH_QT_BUILD_LEGACY_WIDGETS)
     ADD_SUBDIRECTORY(osgearth_package_qt)
 ENDIF()
 
 IF(BUILD_OSGEARTH_EXAMPLES)
     SET(TARGET_DEFAULT_LABEL_PREFIX "Sample")
     SET(TARGET_DEFAULT_APPLICATION_FOLDER "Samples")
-    ADD_SUBDIRECTORY(osgearth_clamp)
     ADD_SUBDIRECTORY(osgearth_manip)
     ADD_SUBDIRECTORY(osgearth_toc)
-    ADD_SUBDIRECTORY(osgearth_createtile)
     ADD_SUBDIRECTORY(osgearth_elevation)
     ADD_SUBDIRECTORY(osgearth_features)
     ADD_SUBDIRECTORY(osgearth_featureinfo)
@@ -64,11 +64,6 @@ IF(BUILD_OSGEARTH_EXAMPLES)
     ADD_SUBDIRECTORY(osgearth_transform)
     ADD_SUBDIRECTORY(osgearth_horizon)
     ADD_SUBDIRECTORY(osgearth_http)
-
-    IF(NOT ${OPENSCENEGRAPH_VERSION} VERSION_LESS "2.9.6")
-        ADD_SUBDIRECTORY(osgearth_featureeditor)
-    ENDIF()
-
     ADD_SUBDIRECTORY(osgearth_measure)
     ADD_SUBDIRECTORY(osgearth_controls)
     ADD_SUBDIRECTORY(osgearth_shadercomp)
@@ -81,27 +76,26 @@ IF(BUILD_OSGEARTH_EXAMPLES)
     ADD_SUBDIRECTORY(osgearth_colorfilter)
     ADD_SUBDIRECTORY(osgearth_sequencecontrol)
     ADD_SUBDIRECTORY(osgearth_minimap)
-    ADD_SUBDIRECTORY(osgearth_sharedlayer)
     ADD_SUBDIRECTORY(osgearth_mrt)
-    ADD_SUBDIRECTORY(osgearth_fog)
     ADD_SUBDIRECTORY(osgearth_shadergen)
     ADD_SUBDIRECTORY(osgearth_clipplane)
     ADD_SUBDIRECTORY(osgearth_cache_test)
     ADD_SUBDIRECTORY(osgearth_pick)
     ADD_SUBDIRECTORY(osgearth_wfs)
     ADD_SUBDIRECTORY(osgearth_datetime)
-    ADD_SUBDIRECTORY(osgearth_pagingtest)
-    ADD_SUBDIRECTORY(osgearth_xfbtest)
     ADD_SUBDIRECTORY(osgearth_ephemeris)
     ADD_SUBDIRECTORY(osgearth_computerangecallback)
-    ADD_SUBDIRECTORY(osgearth_splat)
     ADD_SUBDIRECTORY(osgearth_skyview)
     ADD_SUBDIRECTORY(osgearth_server)
-    ADD_SUBDIRECTORY(osgearth_deformation)
     ADD_SUBDIRECTORY(osgearth_srstest)
+    ADD_SUBDIRECTORY(osgearth_lights)
+    ADD_SUBDIRECTORY(osgearth_noisegen)
+    ADD_SUBDIRECTORY(osgearth_infinitescroll)
+    ADD_SUBDIRECTORY(osgearth_video)
+    ADD_SUBDIRECTORY(osgearth_splat)
+    ADD_SUBDIRECTORY(osgearth_htm)
 
-
-    IF (Qt5Widgets_FOUND OR QT4_FOUND AND NOT ANDROID AND OSGEARTH_USE_QT)
+    IF (Qt5Widgets_FOUND OR QT4_FOUND AND NOT ANDROID AND OSGEARTH_QT_BUILD)
         ADD_SUBDIRECTORY(osgearth_qt_simple)
         ADD_SUBDIRECTORY(osgearth_qt_windows)
     ENDIF()
@@ -113,5 +107,15 @@ IF(BUILD_OSGEARTH_EXAMPLES)
     IF(TRITON_FOUND)
         ADD_SUBDIRECTORY(osgearth_triton)
     ENDIF(TRITON_FOUND)
+
 ENDIF(BUILD_OSGEARTH_EXAMPLES)
 
+ELSE()
+
+    IF(BUILD_OSGEARTH_EXAMPLES)
+        SET(TARGET_DEFAULT_LABEL_PREFIX "Sample")
+        SET(TARGET_DEFAULT_APPLICATION_FOLDER "Samples")
+        ADD_SUBDIRECTORY(osgearth_viewerIOS)
+    ENDIF()
+
+ENDIF()
\ No newline at end of file
diff --git a/src/applications/osgearth_3pv/osgearth_3pv.cpp b/src/applications/osgearth_3pv/osgearth_3pv.cpp
index 36ae763..d88db28 100644
--- a/src/applications/osgearth_3pv/osgearth_3pv.cpp
+++ b/src/applications/osgearth_3pv/osgearth_3pv.cpp
@@ -236,7 +236,7 @@ main( int argc, char** argv )
 
     MapNode* mapNode = MapNode::get(node.get());
 
-    osg::ref_ptr<osg::Image> icon = osgDB::readImageFile("../data/placemark32.png");
+    osg::ref_ptr<osg::Image> icon = osgDB::readRefImageFile("../data/placemark32.png");
     PlaceNode* place = new PlaceNode(mapNode, GeoPoint::INVALID, icon.get(), "");
     place->getOrCreateStateSet()->setRenderBinDetails(10, "DepthSortedBin");
     place->setDynamic(true);
@@ -249,7 +249,7 @@ main( int argc, char** argv )
 
     mapNode->addChild(new HorizonNode());
 
-    viewer.getView(1)->getCamera()->addCullCallback( new VisitorData::Install("osgEarth.Stealth") );
+    viewer.getView(1)->getCamera()->setCullCallback( new VisitorData::Install("osgEarth.Stealth") );
 
     while (!viewer.done())
     {
diff --git a/src/applications/osgearth_annotation/osgearth_annotation.cpp b/src/applications/osgearth_annotation/osgearth_annotation.cpp
index f5e468d..b009860 100644
--- a/src/applications/osgearth_annotation/osgearth_annotation.cpp
+++ b/src/applications/osgearth_annotation/osgearth_annotation.cpp
@@ -33,6 +33,7 @@
 #include <osgEarthAnnotation/LabelNode>
 #include <osgEarthAnnotation/LocalGeometryNode>
 #include <osgEarthAnnotation/FeatureNode>
+#include <osgEarthAnnotation/ModelNode>
 
 #include <osgEarthAnnotation/AnnotationEditing>
 #include <osgEarthAnnotation/ImageOverlayEditor>
@@ -82,14 +83,14 @@ main(int argc, char** argv)
 
     // Group to hold all our annotation elements.
     osg::Group* annoGroup = new osg::Group();
-    root->addChild( annoGroup );
+    MapNode::get(node)->addChild( annoGroup );
 
     // Make a group for labels
     osg::Group* labelGroup = new osg::Group();
     annoGroup->addChild( labelGroup );
 
     osg::Group* editGroup = new osg::Group();
-    root->addChild( editGroup );
+    MapNode::get(node)->addChild( editGroup );
 
     // Style our labels:
     Style labelStyle;
@@ -131,7 +132,16 @@ main(int argc, char** argv)
     //--------------------------------------------------------------------
 
     // a box that follows lines of latitude (rhumb line interpolation, the default)
+    // and flashes on and off using a cull callback.
     {
+        struct C : public osg::NodeCallback {
+            void operator()(osg::Node* n, osg::NodeVisitor* nv) {
+                static int i=0;
+                i++;
+                if (i % 100 < 50)
+                    traverse(n, nv);
+            }
+        };
         Geometry* geom = new Polygon();
         geom->push_back( osg::Vec3d(0,   40, 0) );
         geom->push_back( osg::Vec3d(-60, 40, 0) );
@@ -149,7 +159,9 @@ main(int argc, char** argv)
         geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
         
         FeatureNode* fnode = new FeatureNode(mapNode, feature, geomStyle);
-        
+
+        fnode->addCullCallback(new C());
+
         annoGroup->addChild( fnode );
 
         labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-30, 50), "Rhumb line polygon", labelStyle) );
@@ -204,6 +216,7 @@ main(int argc, char** argv)
         pathStyle.getOrCreate<PointSymbol>()->fill()->color() = Color::Red;
         pathStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
         pathStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;
+        pathStyle.getOrCreate<RenderSymbol>()->depthOffset()->enabled() = true;
 
         //OE_INFO << "Path extent = " << pathFeature->getExtent().toString() << std::endl;
 
@@ -335,10 +348,10 @@ main(int argc, char** argv)
     // an image overlay.
     {
         ImageOverlay* imageOverlay = 0L;
-        osg::Image* image = osgDB::readImageFile( "../data/USFLAG.TGA" );
-        if ( image )
+        osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile( "../data/USFLAG.TGA" );
+        if (image.valid())
         {
-            imageOverlay = new ImageOverlay(mapNode, image);
+            imageOverlay = new ImageOverlay(mapNode, image.get());
             imageOverlay->setBounds( Bounds( -100.0, 35.0, -90.0, 40.0) );
             annoGroup->addChild( imageOverlay );
 
@@ -348,6 +361,18 @@ main(int argc, char** argv)
 
     //--------------------------------------------------------------------
 
+    // a model node with auto scaling.
+    {
+        Style style;
+        style.getOrCreate<ModelSymbol>()->autoScale() = true;
+        style.getOrCreate<ModelSymbol>()->url()->setLiteral("../data/red_flag.osg.50.scale");
+        ModelNode* modelNode = new ModelNode(mapNode, style); 
+        modelNode->setPosition(GeoPoint(geoSRS, -100, 52));
+        annoGroup->addChild(modelNode);
+    }
+
+    //--------------------------------------------------------------------
+
     // initialize the viewer:    
     viewer.setSceneData( root );    
     viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
diff --git a/src/applications/osgearth_atlas/osgearth_atlas.cpp b/src/applications/osgearth_atlas/osgearth_atlas.cpp
index bad44c1..d7feb13 100644
--- a/src/applications/osgearth_atlas/osgearth_atlas.cpp
+++ b/src/applications/osgearth_atlas/osgearth_atlas.cpp
@@ -211,8 +211,8 @@ show(osg::ArgumentParser& arguments)
             osgDB::getFileExtension(atlasFile);
     }
 
-    osg::Image* image = osgDB::readImageFile(atlasFile);
-    if ( !image )
+    osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(atlasFile);
+    if (!image.valid())
         return usage("Failed to load atlas image");
 
     if ( layer > image->r()-1 )
@@ -220,7 +220,7 @@ show(osg::ArgumentParser& arguments)
 
     // geometry for the image layer:
     std::vector<osg::ref_ptr<osg::Image> > images;
-    osgEarth::ImageUtils::flattenImage(image, images);
+    osgEarth::ImageUtils::flattenImage(image.get(), images);
     osg::Geode* geode = osg::createGeodeForImage(images[layer].get());
 
     const osg::BoundingBox& bbox = osgEarth::Utils::getBoundingBox(geode->getDrawable(0));
diff --git a/src/applications/osgearth_boundarygen/BoundaryUtil.cpp b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
index 66fb5e7..5eea478 100644
--- a/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
+++ b/src/applications/osgearth_boundarygen/BoundaryUtil.cpp
@@ -647,5 +647,5 @@ BoundaryUtil::simpleBoundaryTest(const osg::Vec3dArray& boundary)
   outterPoly->push_back(osg::Vec3d(boundsBounds.xMin() - 10.0, boundsBounds.yMax() + 10.0, boundsBounds.zMin()));
 
   osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly;
-  return outterPoly->difference(boundsPoly, outPoly);
+  return outterPoly->difference(boundsPoly.get(), outPoly);
 }
diff --git a/src/applications/osgearth_boundarygen/boundarygen.cpp b/src/applications/osgearth_boundarygen/boundarygen.cpp
index d33f91c..f2c1269 100644
--- a/src/applications/osgearth_boundarygen/boundarygen.cpp
+++ b/src/applications/osgearth_boundarygen/boundarygen.cpp
@@ -22,6 +22,8 @@
 
 #include "BoundaryUtil"
 
+#include <osgEarth/FileUtils>
+
 #include <iostream>
 #include <iomanip>
 #include <fstream>
@@ -80,11 +82,11 @@ int main(int argc, char** argv)
     bool convexOnly = arguments.read("--convex-hull");
     bool view = arguments.read("--view");
 
-    osg::Node* modelNode = osgDB::readNodeFiles( arguments );
-    if (!modelNode)
+    osg::ref_ptr<osg::Node> modelNode = osgDB::readNodeFiles( arguments );
+    if (!modelNode.valid())
         return usage( argv, "Unable to load model." );
 
-    osg::ref_ptr<osg::Vec3dArray> hull = BoundaryUtil::getBoundary(modelNode, geocentric, convexOnly);
+    osg::ref_ptr<osg::Vec3dArray> hull = BoundaryUtil::getBoundary(modelNode.get(), geocentric, convexOnly);
 
     if ( !outFile.empty() )
     {
diff --git a/src/applications/osgearth_city/osgearth_city.cpp b/src/applications/osgearth_city/osgearth_city.cpp
index a6116da..f3f4e0a 100644
--- a/src/applications/osgearth_city/osgearth_city.cpp
+++ b/src/applications/osgearth_city/osgearth_city.cpp
@@ -25,16 +25,21 @@
 #include <osgViewer/Viewer>
 
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ModelLayer>
 
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/AutoClipPlaneHandler>
 #include <osgEarthUtil/LogarithmicDepthBuffer>
 
+#include <osgEarthFeatures/FeatureModelLayer>
+
 #include <osgEarthDrivers/tms/TMSOptions>
 #include <osgEarthDrivers/xyz/XYZOptions>
 #include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 #include <osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions>
+#include <osgEarthDrivers/engine_rex/RexTerrainEngineOptions>
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
@@ -43,7 +48,7 @@ using namespace osgEarth::Symbology;
 using namespace osgEarth::Util;
 
 #define IMAGERY_URL      "http://readymap.org/readymap/tiles/1.0.0/22/"
-#define ELEVATION_URL    "http://readymap.org/readymap/tiles/1.0.0/9/"
+#define ELEVATION_URL    "http://readymap.org/readymap/tiles/1.0.0/116/"
 #define BUILDINGS_URL    "../data/boston_buildings_utm19.shp"
 #define RESOURCE_LIB_URL "../data/resources/textures_us/catalog.xml"
 #define STREETS_URL      "../data/boston-scl-utm19n-meters.shp"
@@ -87,8 +92,13 @@ main(int argc, char** argv)
     osg::Group* root = new osg::Group();
     viewer.setSceneData( root );
 
+    // Pick a terrain engine expressly:
+    RexTerrainEngine::RexTerrainEngineOptions terrainOptions;
+    MapNodeOptions mapNodeOptions;
+    mapNodeOptions.setTerrainOptions(terrainOptions);
+
     // make the map scene graph:
-    MapNode* mapNode = new MapNode( map );
+    MapNode* mapNode = new MapNode(map, mapNodeOptions);
     root->addChild( mapNode );
 
     // zoom to a good startup position
@@ -110,7 +120,7 @@ void addImagery(Map* map)
     // add a TMS imagery layer:
     TMSOptions imagery;
     imagery.url() = IMAGERY_URL;
-    map->addImageLayer( new ImageLayer("ReadyMap imagery", imagery) );
+    map->addLayer( new ImageLayer("ReadyMap imagery", imagery) );
 }
 
 
@@ -119,17 +129,17 @@ void addElevation(Map* map)
     // add a TMS elevation layer:
     TMSOptions elevation;
     elevation.url() = ELEVATION_URL;
-    map->addElevationLayer( new ElevationLayer("ReadyMap elevation", elevation) );
+    map->addLayer( new ElevationLayer("ReadyMap elevation", elevation) );
 }
 
 
 void addBuildings(Map* map)
 {
     // create a feature source to load the building footprint shapefile.
-    OGRFeatureOptions feature_opt;
-    feature_opt.name() = "buildings";
-    feature_opt.url() = BUILDINGS_URL;
-    feature_opt.buildSpatialIndex() = true;
+    OGRFeatureOptions buildingData;
+    buildingData.name() = "buildings";
+    buildingData.url() = BUILDINGS_URL;
+    buildingData.buildSpatialIndex() = true;
     
     // a style for the building data:
     Style buildingStyle;
@@ -181,16 +191,16 @@ void addBuildings(Map* map)
     // the visibility range combine to determine the tile size, such that
     // tile radius = max range / tile size factor.
     FeatureDisplayLayout layout;
-    layout.tileSizeFactor() = 52.0;
+    layout.tileSize() = 500;
     layout.addLevel( FeatureLevel(0.0f, 20000.0f, "buildings") );
 
-    // create a model layer that will render the buildings according to our style sheet.
-    FeatureGeomModelOptions fgm_opt;
-    fgm_opt.featureOptions() = feature_opt;
-    fgm_opt.styles() = styleSheet;
-    fgm_opt.layout() = layout;
+    FeatureModelLayer* layer = new FeatureModelLayer();
+    layer->setName("Buildings");
+    layer->options().featureSource() = buildingData;
+    layer->options().styles() = styleSheet;
+    layer->options().layout() = layout;
 
-    map->addModelLayer( new ModelLayer( "buildings", fgm_opt ) );
+    map->addLayer(layer);
 }
 
 
@@ -233,27 +243,28 @@ void addStreets(Map* map)
     // Set up a paging layout. The tile size factor and the visibility range combine
     // to determine the tile size, such that tile radius = max range / tile size factor.
     FeatureDisplayLayout layout;
-    layout.tileSizeFactor() = 7.5f;
-    layout.maxRange()       = 5000.0f;
+    layout.tileSize() = 500;
+    layout.maxRange() = 5000.0f;
 
     // create a model layer that will render the buildings according to our style sheet.
-    FeatureGeomModelOptions fgm_opt;
-    fgm_opt.featureOptions() = feature_opt;
-    fgm_opt.layout() = layout;
-    fgm_opt.styles() = new StyleSheet();
-    fgm_opt.styles()->addStyle( style );
-
-    map->addModelLayer( new ModelLayer("streets", fgm_opt) );
+    FeatureModelLayerOptions streets;
+    streets.name() = "streets";
+    streets.featureSource() = feature_opt;
+    streets.layout() = layout;
+    streets.styles() = new StyleSheet();
+    streets.styles()->addStyle( style );
+
+    map->addLayer(new FeatureModelLayer(streets));
 }
 
 
 void addParks(Map* map)
 {
     // create a feature source to load the shapefile.
-    OGRFeatureOptions feature_opt;
-    feature_opt.name() = "parks";
-    feature_opt.url() = PARKS_URL;
-    feature_opt.buildSpatialIndex() = true;
+    OGRFeatureOptions parksData;
+    parksData.name() = "parks";
+    parksData.url() = PARKS_URL;
+    parksData.buildSpatialIndex() = true;
 
     // a style:
     Style style;
@@ -264,10 +275,11 @@ void addParks(Map* map)
     // data are polygons, the PLACEMENT_RANDOM directive below will scatter
     // points within the polygon boundary at the specified density.
     ModelSymbol* model = style.getOrCreate<ModelSymbol>();
-    model->url()->setLiteral(TREE_MODEL_URL);
+    //model->url()->setLiteral(TREE_MODEL_URL);
     model->scale()->setLiteral( 0.2 );
     model->placement() = model->PLACEMENT_RANDOM;
     model->density() = 3000.0f; // instances per sqkm
+    model->setModel(osgDB::readRefNodeFile(TREE_MODEL_URL).release());
     
     // Clamp to the terrain:
     AltitudeSymbol* alt = style.getOrCreate<AltitudeSymbol>();
@@ -277,21 +289,25 @@ void addParks(Map* map)
     // that's sufficiently transparent; this will prevent depth-sorting anomolies
     // common when rendering lots of semi-transparent objects.
     RenderSymbol* render = style.getOrCreate<RenderSymbol>();
+    render->transparent() = true;
     render->minAlpha() = 0.15f;
 
     // Set up a paging layout. The tile size factor and the visibility range combine
     // to determine the tile size, such that tile radius = max range / tile size factor.
     FeatureDisplayLayout layout;
-    layout.tileSizeFactor() = 3.0f;
-    layout.maxRange()       = 2000.0f;
+    layout.tileSize() = 650;
+    layout.maxRange() = 2000.0f;
 
     // create a model layer that will render the buildings according to our style sheet.
-    FeatureGeomModelOptions fgm_opt;
-    fgm_opt.featureOptions() = feature_opt;
-    fgm_opt.layout() = layout;
-    fgm_opt.styles() = new StyleSheet();
-    fgm_opt.styles()->addStyle( style );
-    fgm_opt.compilerOptions().instancing() = true;
-
-    map->addModelLayer( new ModelLayer("parks", fgm_opt) );
+    FeatureModelLayerOptions parks;
+    parks.name() = "parks";
+    parks.featureSource() = parksData;
+    parks.layout() = layout;
+    parks.styles() = new StyleSheet();
+    parks.styles()->addStyle( style );
+
+    parks.instancing() = true;
+    parks.clusterCulling() = false;
+
+    map->addLayer(new FeatureModelLayer(parks));
 }
diff --git a/src/applications/osgearth_clamp/osgearth_clamp.cpp b/src/applications/osgearth_clamp/osgearth_clamp.cpp
deleted file mode 100644
index d813544..0000000
--- a/src/applications/osgearth_clamp/osgearth_clamp.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2016 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-* IN THE SOFTWARE.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osg/Notify>
-#include <osgGA/StateSetManipulator>
-#include <osgGA/GUIEventHandler>
-#include <osgViewer/Viewer>
-#include <osgViewer/ViewerEventHandlers>
-#include <osgEarth/MapNode>
-#include <osgEarth/Terrain>
-#include <osgEarth/XmlUtils>
-#include <osgEarth/Viewpoint>
-#include <osgEarthUtil/EarthManipulator>
-#include <osgEarthUtil/AutoClipPlaneHandler>
-#include <osgEarthUtil/ObjectLocator>
-
-using namespace osgEarth;
-using namespace osgEarth::Util;
-
-class ClampObjectLocatorCallback : public osgEarth::TerrainCallback
-{
-public:
-    ClampObjectLocatorCallback(ObjectLocatorNode* locator):
-      _locator(locator),
-      _maxLevel(-1),
-      _minLevel(0)
-    {
-    }
-
-    virtual void onTileAdded(const osgEarth::TileKey& tileKey, osg::Node* terrain, TerrainCallbackContext&)
-    {           
-        if ((int)tileKey.getLevelOfDetail() > _minLevel && _maxLevel < (int)tileKey.getLevelOfDetail())
-        {
-            osg::Vec3d position = _locator->getLocator()->getPosition();
-
-            if (tileKey.getExtent().contains(position.x(), position.y()))
-            {
-                //Compute our location in geocentric
-                const osg::EllipsoidModel* ellipsoid = tileKey.getProfile()->getSRS()->getEllipsoid();
-                double x, y, z;            
-                ellipsoid->convertLatLongHeightToXYZ(
-                    osg::DegreesToRadians(position.y()), osg::DegreesToRadians(position.x()), 0,
-                    x, y, z);
-                //Compute the up vector
-                osg::Vec3d up = ellipsoid->computeLocalUpVector(x, y, z );
-                up.normalize();
-                osg::Vec3d world(x, y, z);
-
-                double segOffset = 50000;
-
-                osg::Vec3d start = world + (up * segOffset);
-                osg::Vec3d end = world - (up * segOffset);
-
-                osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
-
-                osgUtil::IntersectionVisitor iv;            
-                iv.setIntersector( i );
-                terrain->accept( iv );
-
-                osgUtil::LineSegmentIntersector::Intersections& results = i->getIntersections();
-                if ( !results.empty() )
-                {
-                    const osgUtil::LineSegmentIntersector::Intersection& result = *results.begin();
-                    osg::Vec3d hit = result.getWorldIntersectPoint();
-                    double lat, lon, height;
-                    ellipsoid->convertXYZToLatLongHeight(hit.x(), hit.y(), hit.z(), 
-                        lat, lon, height);                
-                    position.z() = height;
-                    //OE_NOTICE << "Got hit, setting new height to " << height << std::endl;
-                    _maxLevel = tileKey.getLevelOfDetail();
-                    _locator->getLocator()->setPosition( position );
-                }            
-            }            
-        }            
-
-    }
-
-    osg::ref_ptr< ObjectLocatorNode > _locator;
-    int _maxLevel;
-    int _minLevel;
-};
-
-
-
-int
-main(int argc, char** argv)
-{
-    osg::ArgumentParser arguments(&argc,argv);
-    osgViewer::Viewer viewer(arguments);
-
-    unsigned int numObjects = 5000;
-    while (arguments.read("--count", numObjects)) {}
-
-
-    // load the .earth file from the command line.
-    osg::Node* earthNode = osgDB::readNodeFiles( arguments );
-    if (!earthNode)
-    {
-        OE_NOTICE << "Unable to load earth model" << std::endl;
-        return 1;
-    }
-
-    osg::Group* root = new osg::Group();
-
-    osgEarth::MapNode * mapNode = osgEarth::MapNode::findMapNode( earthNode );
-    if (!mapNode)
-    {
-        OE_NOTICE << "Could not find MapNode " << std::endl;
-        return 1;
-    }
-
-    osgEarth::Util::EarthManipulator* manip = new EarthManipulator();
-    manip->getSettings()->setArcViewpointTransitions( true );
-    viewer.setCameraManipulator( manip );
-    
-    root->addChild( earthNode );    
-
-    //viewer.getCamera()->addCullCallback( new AutoClipPlaneCullCallback(mapNode->getMap()) );
-
-    
-    osg::Node* tree = osgDB::readNodeFile("../data/tree.osg");         
-    osg::MatrixTransform* mt = new osg::MatrixTransform();
-    mt->setMatrix(osg::Matrixd::scale(10,10,10));
-    mt->addChild( tree );
-    //Create bound around mt rainer
-    double centerLat =  46.840866;
-    double centerLon = -121.769846;
-    double height = 0.2;
-    double width = 0.2;
-    double minLat = centerLat - (height/2.0);
-    double minLon = centerLon - (width/2.0);
-
-    OE_NOTICE << "Placing " << numObjects << " trees" << std::endl;
-
-    for (unsigned int i = 0; i < numObjects; i++)
-    {
-        osgEarth::Util::ObjectLocatorNode* locator = new osgEarth::Util::ObjectLocatorNode( mapNode->getMap() );        
-        double lat = minLat + height * (rand() * 1.0)/(RAND_MAX-1);
-        double lon = minLon + width * (rand() * 1.0)/(RAND_MAX-1);        
-        //OE_NOTICE << "Placing tree at " << lat << ", " << lon << std::endl;
-        locator->getLocator()->setPosition(osg::Vec3d(lon,  lat, 0 ) );        
-        locator->addChild( mt );
-        root->addChild( locator );
-        mapNode->getTerrain()->addTerrainCallback( new ClampObjectLocatorCallback(locator) );        
-    }    
-    
-    manip->setHomeViewpoint(Viewpoint("Home", centerLon, centerLat, 0.0, 0.0, -90, 45000 ));
-
-    viewer.setSceneData( root );    
-
-    // add some stock OSG handlers:
-    viewer.addEventHandler(new osgViewer::StatsHandler());
-    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
-    viewer.addEventHandler(new osgViewer::ThreadingHandler());
-    viewer.addEventHandler(new osgViewer::LODScaleHandler());
-    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
-
-    return viewer.run();
-}
diff --git a/src/applications/osgearth_clipplane/osgearth_clipplane.cpp b/src/applications/osgearth_clipplane/osgearth_clipplane.cpp
index a12f981..e64a441 100644
--- a/src/applications/osgearth_clipplane/osgearth_clipplane.cpp
+++ b/src/applications/osgearth_clipplane/osgearth_clipplane.cpp
@@ -89,7 +89,7 @@ main(int argc, char** argv)
 
         // Install a ClipNode. The ClipNode establishes positional state so it
         // doesn't need to parent anything. In this case it needs to be at the
-        // top of the scene graph since out clip plane calculator assumes 
+        // top of the scene graph since our clip plane calculator assumes 
         // you're in world space.
         osg::ClipNode* clipNode = new osg::ClipNode();
         root->addChild( clipNode );
diff --git a/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp b/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
index fe65f48..6f2c8b1 100644
--- a/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
+++ b/src/applications/osgearth_colorfilter/osgearth_colorfilter.cpp
@@ -22,6 +22,7 @@
 
 #include <osgViewer/Viewer>
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
@@ -700,16 +701,18 @@ main(int argc, char** argv)
     osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( node );
     if ( node )
     {   
-        if (mapNode->getMap()->getNumImageLayers() == 0)
+        ImageLayerVector imageLayers;
+        mapNode->getMap()->getLayers(imageLayers);
+
+        if (imageLayers.empty())
         {
             return usage("Please provide a map with at least one image layer.");
         }
 
         // attach color filter to each layer.
-        unsigned numLayers = mapNode->getMap()->getNumImageLayers();
-        for( unsigned i=0; i<numLayers; ++i )
+        for (unsigned i = 0; i<imageLayers.size(); ++i)
         {
-            ImageLayer* layer = mapNode->getMap()->getImageLayerAt( i );
+            ImageLayer* layer = imageLayers[i].get();
 
             if ( layer->getEnabled() && layer->getVisible() )
             {
diff --git a/src/applications/osgearth_computerangecallback/osgearth_computerangecallback.cpp b/src/applications/osgearth_computerangecallback/osgearth_computerangecallback.cpp
index be37189..0f5fd92 100644
--- a/src/applications/osgearth_computerangecallback/osgearth_computerangecallback.cpp
+++ b/src/applications/osgearth_computerangecallback/osgearth_computerangecallback.cpp
@@ -60,6 +60,7 @@ struct MyComputeRangeCallback : public osgEarth::ComputeRangeCallback
             double angularSize = osg::RadiansToDegrees( 2.0*atan(radius/distance) );
             double dpp = osg::maximum(fov, 1.0e-17) / viewPort->height();
             float pixelSize = angularSize / dpp;
+            //OE_NOTICE << "Returning " << pixelSize << std::endl;
             return pixelSize;
         }
 
diff --git a/src/applications/osgearth_controls/osgearth_controls.cpp b/src/applications/osgearth_controls/osgearth_controls.cpp
index 9220327..c1c580f 100644
--- a/src/applications/osgearth_controls/osgearth_controls.cpp
+++ b/src/applications/osgearth_controls/osgearth_controls.cpp
@@ -112,7 +112,7 @@ createControls( ControlCanvas* cs )
         center->setVertAlign( Control::ALIGN_CENTER );
 
         // Add an image:
-        osg::ref_ptr<osg::Image> image = osgDB::readImageFile("../data/osgearth.gif");
+        osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile("../data/osgearth.gif");
         if ( image.valid() )
         {
             s_imageControl = new ImageControl( image.get() );
diff --git a/src/applications/osgearth_conv/osgearth_conv.cpp b/src/applications/osgearth_conv/osgearth_conv.cpp
index 052e3b3..9dd5205 100644
--- a/src/applications/osgearth_conv/osgearth_conv.cpp
+++ b/src/applications/osgearth_conv/osgearth_conv.cpp
@@ -26,9 +26,13 @@
 #include <osgEarth/TileSource>
 #include <osgEarth/TileHandler>
 #include <osgEarth/TileVisitor>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
 #include <osg/ArgumentParser>
 #include <osg/Timer>
 #include <iomanip>
+#include <algorithm>
+#include <iterator>
 
 using namespace osgEarth;
 
@@ -47,49 +51,11 @@ int usage(char** argv)
         << "\n    --osg-options [OSG options string]  : options to pass to OSG readers/writers"
         << "\n    --extents [minLat] [minLong] [maxLat] [maxLong] : Lat/Long extends to copy"
         << std::endl;
-        
+
     return 0;
 }
 
 
-// TileHandler that copies images from one tilesource to another.
-struct TileSourceToTileSource : public TileHandler
-{
-    TileSourceToTileSource(TileSource* source, TileSource* dest, bool heightFields)
-        : _source(source), _dest(dest), _heightFields(heightFields)
-    {
-        //nop
-    }
-
-    bool handleTile(const TileKey& key, const TileVisitor& tv)
-    {
-        bool ok = false;
-        if (_heightFields)
-        {
-            osg::ref_ptr<osg::HeightField> hf = _source->createHeightField(key);
-            if ( hf.valid() )
-                ok = _dest->storeHeightField(key, hf.get(), 0L);
-        }
-        else
-        {
-            osg::ref_ptr<osg::Image> image = _source->createImage(key);
-            if ( image.valid() )
-                ok = _dest->storeImage(key, image.get(), 0L);
-        }
-        return ok;
-    }
-    
-    bool hasData(const TileKey& key) const
-    {
-        return _source->hasData(key);
-    }
-
-    TileSource* _source;
-    TileSource* _dest;
-    bool        _heightFields;
-};
-
-
 // TileHandler that copies images from an ImageLayer to a TileSource.
 // This will automatically handle any mosaicing and reprojection that is
 // necessary to translate from one Profile/SRS to another.
@@ -106,14 +72,14 @@ struct ImageLayerToTileSource : public TileHandler
         bool ok = false;
         GeoImage image = _source->createImage(key);
         if (image.valid())
-            ok = _dest->storeImage(key, image.getImage(), 0L);        
+            ok = _dest->storeImage(key, image.getImage(), 0L);
 
         return ok;
     }
-    
+
     bool hasData(const TileKey& key) const
     {
-        return _source->getTileSource()->hasData(key);
+        return _source->mayHaveDataInExtent(key.getExtent());
     }
 
     osg::ref_ptr<ImageLayer> _source;
@@ -140,10 +106,10 @@ struct ElevationLayerToTileSource : public TileHandler
             ok = _dest->storeHeightField(key, hf.getHeightField(), 0L);
         return ok;
     }
-    
+
     bool hasData(const TileKey& key) const
     {
-        return _source->getTileSource()->hasData(key);
+        return _source->mayHaveDataInExtent(key.getExtent());
     }
 
     osg::ref_ptr<ElevationLayer> _source;
@@ -154,21 +120,42 @@ struct ElevationLayerToTileSource : public TileHandler
 // Custom progress reporter
 struct ProgressReporter : public osgEarth::ProgressCallback
 {
-    bool reportProgress(double             current, 
-                        double             total, 
+    ProgressReporter() : _first(true) { }
+
+    bool reportProgress(double             current,
+                        double             total,
                         unsigned           currentStage,
                         unsigned           totalStages,
                         const std::string& msg )
     {
         _mutex.lock();
 
-        float percentage = current/total*100.0f;
-        std::cout 
+        if (_first)
+        {
+            _first = false;
+            _start = osg::Timer::instance()->tick();
+        }
+        osg::Timer_t now = osg::Timer::instance()->tick();
+
+        
+
+        float percentage = current/total;
+
+        double timeSoFar = osg::Timer::instance()->delta_s(_start, now);
+        double projectedTotalTime = timeSoFar/percentage;
+        double timeToGo = projectedTotalTime - timeSoFar;
+        double minsToGo = timeToGo/60.0;
+        double secsToGo = fmod(timeToGo,60.0);
+        double minsTotal = projectedTotalTime/60.0;
+        double secsTotal = fmod(projectedTotalTime,60.0);
+
+        std::cout
             << std::fixed
-            << std::setprecision(1) << "\r" 
+            << std::setprecision(1) << "\r"
             << (int)current << "/" << (int)total
-            << " (" << percentage << "%)"
-            << "                        "
+            << " (" << (100.0f*percentage) << "%, " 
+            << (int)minsTotal << "m" << (int)secsTotal << "s projected, "
+            << (int)minsToGo << "m" << (int)secsToGo << "s remaining)        "
             << std::flush;
 
         if ( percentage >= 100.0f )
@@ -180,6 +167,8 @@ struct ProgressReporter : public osgEarth::ProgressCallback
     }
 
     Threading::Mutex _mutex;
+    bool _first;
+    osg::Timer_t _start;
 };
 
 
@@ -235,7 +224,7 @@ main(int argc, char** argv)
         inConf.set(key, value);
 
     osg::ref_ptr<osgDB::Options> dbo = new osgDB::Options();
-    
+
     // plugin options, if the user passed them in:
     std::string str;
     while(args.read("--osg-options", str) || args.read("-O", str))
@@ -254,7 +243,7 @@ main(int argc, char** argv)
     Status inputStatus = input->open( input->MODE_READ, dbo.get() );
     if ( inputStatus.isError() )
     {
-        OE_WARN << LC << "Error initializing input" << std::endl;
+        OE_WARN << LC << "Error initializing input: " << inputStatus.message() << std::endl;
         return -1;
     }
 
@@ -295,17 +284,29 @@ main(int argc, char** argv)
     osg::ref_ptr<TileSource> output = TileSourceFactory::create(outOptions);
     if ( !output.valid() )
     {
-        OE_WARN << LC << "Failed to open output" << std::endl;
+        OE_WARN << LC << "Failed to open output." << std::endl;
         return -1;
     }
 
+    // Copy over the data extents to the output datasource.
+    for (DataExtentList::const_iterator itr = input->getDataExtents().begin(); itr != input->getDataExtents().end(); ++itr)
+    {
+        // Convert the data extent to the profile that is actually used by the output tile source
+        DataExtent dataExtent = *itr;
+        GeoExtent ext = dataExtent.transform(outputProfile->getSRS());
+        unsigned int minLevel = 0;
+        unsigned int maxLevel = outputProfile->getEquivalentLOD( input->getProfile(), *dataExtent.maxLevel() );
+        DataExtent outputExtent = DataExtent(ext, minLevel, maxLevel);
+        output->getDataExtents().push_back( outputExtent );
+    }
+
     Status outputStatus = output->open(
         TileSource::MODE_WRITE | TileSource::MODE_CREATE,
         dbo.get() );
 
     if ( outputStatus.isError() )
     {
-        OE_WARN << LC << "Error initializing output" << std::endl;
+        OE_WARN << LC << "Error initializing output: " << outputStatus.message() << std::endl;
         return -1;
     }
 
@@ -333,50 +334,50 @@ main(int argc, char** argv)
         visitor = new TileVisitor();
     }
 
-    // If the profiles are identical, just use a tile copier.
-    if ( isSameProfile )
+    if (heightFields)
     {
-        OE_NOTICE << LC << "Profiles match - initiating simple tile copy" << std::endl;
-        visitor->setTileHandler( new TileSourceToTileSource(input.get(), output.get(), heightFields) );
+        ElevationLayer* layer = new ElevationLayer(ElevationLayerOptions(), input.get());
+        Status layerStatus = layer->open();
+        if (layerStatus.isError())
+        {
+            OE_WARN << "Failed to create input ElevationLayer " << layerStatus.message() << std::endl;
+            return -1;
+        }
+        if ( !layer->getProfile() || !layer->getProfile()->isOK() )
+        {
+            OE_WARN << LC << "Input profile is not valid" << std::endl;
+            return -1;
+        }
+        visitor->setTileHandler( new ElevationLayerToTileSource(layer, output.get()) );
     }
-    else
-    {
-        OE_NOTICE << LC << "Profiles differ - initiating tile transformation" << std::endl;
 
-        if (heightFields)
+    else // image layers
+    {
+        ImageLayer* layer = new ImageLayer(ImageLayerOptions(), input.get());
+        Status layerStatus = layer->open();
+        if (layerStatus.isError())
         {
-            ElevationLayer* layer = new ElevationLayer(ElevationLayerOptions(), input.get());
-            Status layerStatus = layer->open();
-            if (layerStatus.isError())
-            {
-                OE_WARN << "Failed to create input ElevationLayer " << layerStatus.message() << std::endl;
-                return -1;
-            }
-            if ( !layer->getProfile() || !layer->getProfile()->isOK() )
-            {
-                OE_WARN << LC << "Input profile is not valid" << std::endl;
-                return -1;
-            }
-            visitor->setTileHandler( new ElevationLayerToTileSource(layer, output.get()) );
+            OE_WARN << "Failed to create input ImageLayer " << layerStatus.message() << std::endl;
+            return -1;
         }
-        else
+        if ( !layer->getProfile() || !layer->getProfile()->isOK() )
         {
-            ImageLayer* layer = new ImageLayer(ImageLayerOptions(), input.get());
-            Status layerStatus = layer->open();
-            if (layerStatus.isError())
-            {
-                OE_WARN << "Failed to create input ImageLayer " << layerStatus.message() << std::endl;
-                return -1;
-            }
-            if ( !layer->getProfile() || !layer->getProfile()->isOK() )
-            {
-                OE_WARN << LC << "Input profile is not valid" << std::endl;
-                return -1;
-            }
-            visitor->setTileHandler( new ImageLayerToTileSource(layer, output.get()) );
+            OE_WARN << LC << "Input profile is not valid" << std::endl;
+            return -1;
         }
+        visitor->setTileHandler( new ImageLayerToTileSource(layer, output.get()) );
     }
-    
+
+    // set the manula extents, if specified:
+    bool userSetExtents = false;
+    double minlat, minlon, maxlat, maxlon;
+    while( args.read("--extents", minlat, minlon, maxlat, maxlon) )
+    {
+        GeoExtent extent(SpatialReference::get("wgs84"), minlon, minlat, maxlon, maxlat);
+        visitor->addExtent( extent );
+        userSetExtents = true;
+    }
+
     // Set the level limits:
     unsigned minLevel = ~0;
     bool minLevelSet = args.read("--min-level", minLevel);
@@ -395,9 +396,14 @@ main(int argc, char** argv)
                 maxLevel = i->maxLevel().value();
             if ( !minLevelSet && i->minLevel().isSet() && i->minLevel().value() < minLevel )
                 minLevel = i->minLevel().value();
+
+            if (userSetExtents == false)
+            {
+                visitor->addExtent(*i);
+            }
         }
     }
-       
+
     if ( minLevel < ~0 )
     {
         visitor->setMinLevel( minLevel );
@@ -410,14 +416,6 @@ main(int argc, char** argv)
         OE_NOTICE << LC << "Calculated max level = " << maxLevel << std::endl;
     }
 
-    // set the extents:
-    double minlat, minlon, maxlat, maxlon;
-    while( args.read("--extents", minlat, minlon, maxlat, maxlon) )
-    {
-        GeoExtent extent(SpatialReference::get("wgs84"), minlon, minlat, maxlon, maxlat);
-        visitor->addExtent( extent );
-    }
-
     // Ready!!!
     std::cout << "Working..." << std::endl;
 
@@ -430,7 +428,7 @@ main(int argc, char** argv)
     osg::Timer_t t1 = osg::Timer::instance()->tick();
 
     std::cout
-        << "Time = " 
+        << "Time = "
         << std::fixed
         << std::setprecision(1)
         << osg::Timer::instance()->delta_s(t0, t1)
diff --git a/src/applications/osgearth_createtile/osgearth_createtile.cpp b/src/applications/osgearth_createtile/osgearth_createtile.cpp
index 56806ba..6084c0c 100644
--- a/src/applications/osgearth_createtile/osgearth_createtile.cpp
+++ b/src/applications/osgearth_createtile/osgearth_createtile.cpp
@@ -33,13 +33,11 @@
 #include <osgUtil/LineSegmentIntersector>
 #include <osgEarth/MapNode>
 #include <osgEarth/TerrainEngineNode>
-#include <osgEarth/ElevationQuery>
 #include <osgEarth/StringUtils>
 #include <osgEarth/Terrain>
 #include <osgEarth/GeoTransform>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
-#include <osgEarthUtil/LatLongFormatter>
 #include <osgEarthUtil/ExampleResources>
 #include <osg/TriangleFunctor>
 #include <osgDB/WriteFile>
@@ -58,7 +56,11 @@ struct CollectTriangles
     {
         verts = new osg::Vec3Array();
     }
+#if OSG_VERSION_LESS_THAN(3,5,6)
     inline void operator () (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3, bool treatVertexDataAsTemporary)
+#else
+    inline void operator () (const osg::Vec3& v1,const osg::Vec3& v2,const osg::Vec3& v3)
+#endif
     {
         verts->push_back(v1);
         verts->push_back(v2);
@@ -211,7 +213,7 @@ struct CreateTileHandler : public osgGA::GUIEventHandler
 
                 // Clamp the marker to the intersection of the triangles created by osgEarth.  This should line up with the mesh that is actually rendered.
                 double z = 0.0;
-                s_mapNode->getTerrain()->getHeight( node, s_mapNode->getMapSRS(), mapPoint.x(), mapPoint.y(), &z);
+                s_mapNode->getTerrain()->getHeight( node.get(), s_mapNode->getMapSRS(), mapPoint.x(), mapPoint.y(), &z);
 
                 GeoTransform* xform = new GeoTransform();
                 xform->setPosition( osgEarth::GeoPoint(s_mapNode->getMapSRS(),mapPoint.x(),  mapPoint.y(), z, ALTMODE_ABSOLUTE) );
diff --git a/src/applications/osgearth_datetime/osgearth_datetime.cpp b/src/applications/osgearth_datetime/osgearth_datetime.cpp
index 2df042b..9bec3a7 100644
--- a/src/applications/osgearth_datetime/osgearth_datetime.cpp
+++ b/src/applications/osgearth_datetime/osgearth_datetime.cpp
@@ -166,7 +166,7 @@ main(int argc, char** argv)
     
     osg::ref_ptr<CullNodeByDateTimeRange> callback = new CullNodeByDateTimeRange;
     
-    AssignCullCallbakVisitor assignVisitor(callback);
+    AssignCullCallbakVisitor assignVisitor(callback.get());
     assignVisitor.setNodeMaskOverride(~0);
     node->accept(assignVisitor);
 
@@ -178,7 +178,7 @@ main(int argc, char** argv)
     slider->setBackColor(.6, 0, 0, 1);
     slider->setHeight(25);
     slider->setWidth(300);    
-    slider->addEventHandler(new TimeSliderHandler(callback, collectVisitor.range));
+    slider->addEventHandler(new TimeSliderHandler(callback.get(), collectVisitor.range));
     slider->setValue(0.0);
     cs->addControl(slider);
 
diff --git a/src/applications/osgearth_deformation/osgearth_deformation.cpp b/src/applications/osgearth_deformation/osgearth_deformation.cpp
index fe13231..42ad66f 100644
--- a/src/applications/osgearth_deformation/osgearth_deformation.cpp
+++ b/src/applications/osgearth_deformation/osgearth_deformation.cpp
@@ -339,12 +339,10 @@ struct DeformationHandler : public osgGA::GUIEventHandler
         _tool(TOOL_CIRCLE),
         _root(root),
         _offset(-100.0f),
-        _radius(100.0),
-        _query( s_mapNode->getMap() )
+        _radius(100.0)
     {
         _map = s_mapNode->getMap();
-        _query.setMaxTilesToCache(10);
-        _query.setFallBackOnNoData( false );
+        _envelope = _map->getElevationPool()->createEnvelope(_map->getSRS(), 12u);
     }
 
     void update( float x, float y, osgViewer::View* view )
@@ -364,16 +362,17 @@ struct DeformationHandler : public osgGA::GUIEventHandler
             mapPoint.z() = 0;
 
             // do an elevation query:
-            double query_resolution = 0; // max.
-            double out_hamsl        = 0.0;
-            double out_resolution   = 0.0;
-
-            bool ok = _query.getElevation( 
-                mapPoint,
-                out_hamsl,
-                query_resolution, 
-                &out_resolution );
-            mapPoint.z() = out_hamsl;
+            typedef std::pair<float, float> ElAndRes;
+            ElAndRes elAndRes = _envelope->getElevationAndResolution(mapPoint.x(), mapPoint.y());
+
+            float hamsl = elAndRes.first;
+            float res = elAndRes.second;
+
+            if (hamsl != NO_DATA_VALUE)
+            {
+                mapPoint.z() = hamsl;
+            }
+
             _mapPoint = mapPoint;
 
             
@@ -523,7 +522,7 @@ struct DeformationHandler : public osgGA::GUIEventHandler
                 else if (_tool == TOOL_BLAST)
                 {
                     // Apply a simple blast radius
-                    applyBlast(_mapPoint, _radius, -_radius, itr->first, itr->second);
+                    applyBlast(_mapPoint, _radius, -_radius, itr->first, itr->second.get());
                 }
                 s_deformations->addHeightField( itr->first, itr->second.get());
             }
@@ -543,7 +542,8 @@ struct DeformationHandler : public osgGA::GUIEventHandler
     double _radius;
     GeoPoint _mapPoint;
     osg::ref_ptr < FeatureNode > _featureNode;
-    ElevationQuery   _query;
+    //ElevationQuery   _query;
+    osg::ref_ptr<ElevationEnvelope> _envelope;
 };
 
 
@@ -577,7 +577,7 @@ int main(int argc, char** argv)
 
     ElevationLayer* layer = new ElevationLayer(elevationOpt, s_deformations);
     layer->open();
-    s_mapNode->getMap()->addElevationLayer(layer);
+    s_mapNode->getMap()->addLayer(layer);
 
     osg::Group* root = new osg::Group();
     viewer.setSceneData( root );
diff --git a/src/applications/osgearth_elevation/osgearth_elevation.cpp b/src/applications/osgearth_elevation/osgearth_elevation.cpp
index 883d0a9..c9ad19c 100644
--- a/src/applications/osgearth_elevation/osgearth_elevation.cpp
+++ b/src/applications/osgearth_elevation/osgearth_elevation.cpp
@@ -27,7 +27,6 @@
 #include <osgUtil/LineSegmentIntersector>
 #include <osgEarth/MapNode>
 #include <osgEarth/TerrainEngineNode>
-#include <osgEarth/ElevationQuery>
 #include <osgEarth/StringUtils>
 #include <osgEarth/Terrain>
 #include <osgEarth/VerticalDatum>
@@ -35,7 +34,7 @@
 #include <osgEarthUtil/Controls>
 #include <osgEarthUtil/LatLongFormatter>
 #include <osgEarthUtil/ExampleResources>
-#include <osgEarthAnnotation/PlaceNode>
+#include <osgEarthAnnotation/ModelNode>
 #include <iomanip>
 
 using namespace osgEarth;
@@ -51,7 +50,7 @@ static LabelControl*  s_haeLabel    = 0L;
 static LabelControl*  s_egm96Label  = 0L;
 static LabelControl*  s_mapLabel    = 0L;
 static LabelControl*  s_resLabel    = 0L;
-static PlaceNode*     s_marker      = 0L;
+static ModelNode*     s_marker      = 0L;
 
 
 // An event handler that will print out the elevation at the clicked point
@@ -59,13 +58,11 @@ struct QueryElevationHandler : public osgGA::GUIEventHandler
 {
     QueryElevationHandler()
         : _mouseDown( false ),
-          _terrain  ( s_mapNode->getTerrain() ),
-          _query    ( s_mapNode->getMap() )
+          _terrain  ( s_mapNode->getTerrain() )
     {
         _map = s_mapNode->getMap();
-        _query.setMaxTilesToCache(10);
-        _query.setFallBackOnNoData( false );
         _path.push_back( s_mapNode->getTerrainEngine() );
+        _envelope = _map->getElevationPool()->createEnvelope(_map->getSRS(), 20u);
     }
 
     void update( float x, float y, osgViewer::View* view )
@@ -84,20 +81,20 @@ struct QueryElevationHandler : public osgGA::GUIEventHandler
             mapPoint.fromWorld( _terrain->getSRS(), world );
 
             // do an elevation query:
-            double query_resolution = 0; // max.
-            double out_hamsl        = 0.0;
-            double out_resolution   = 0.0;
+            double query_resolution  = 0.0;  // max.
+            double actual_resolution = 0.0;
+            float elevation          = 0.0f;
 
-            bool ok = _query.getElevation( 
-                mapPoint,
-                out_hamsl,
-                query_resolution, 
-                &out_resolution );
+            std::pair<float, float> result = _envelope->getElevationAndResolution(
+                mapPoint.x(), mapPoint.y());
 
-            if ( ok )
+            elevation = result.first;
+            actual_resolution = result.second;
+
+            if ( elevation != NO_DATA_VALUE )
             {
                 // convert to geodetic to get the HAE:
-                mapPoint.z() = out_hamsl;
+                mapPoint.z() = elevation;
                 GeoPoint mapPointGeodetic( s_mapNode->getMapSRS()->getGeodeticSRS(), mapPoint );
 
                 static LatLongFormatter s_f;
@@ -108,9 +105,15 @@ struct QueryElevationHandler : public osgGA::GUIEventHandler
                     << ", " 
                     << s_f.format(mapPointGeodetic.x(), false) );
 
-                s_mslLabel->setText( Stringify() << out_hamsl );
-                s_haeLabel->setText( Stringify() << mapPointGeodetic.z() );
-                s_resLabel->setText( Stringify() << out_resolution );
+                if (s_mapNode->getMapSRS()->isGeographic())
+                {
+                    double metersPerDegree = s_mapNode->getMapSRS()->getEllipsoid()->getRadiusEquator() / 360.0;
+                    actual_resolution *= metersPerDegree * cos(osg::DegreesToRadians(mapPoint.y()));
+                }
+
+                s_mslLabel->setText( Stringify() << elevation << " m" );
+                s_haeLabel->setText( Stringify() << mapPointGeodetic.z() << " m" );
+                s_resLabel->setText( Stringify() << actual_resolution << " m" );
 
                 double egm96z = mapPoint.z();
 
@@ -121,18 +124,23 @@ struct QueryElevationHandler : public osgGA::GUIEventHandler
                     mapPointGeodetic.x(),
                     egm96z);
                 
-                s_egm96Label->setText(Stringify() << egm96z);
+                s_egm96Label->setText(Stringify() << egm96z << " m");
 
                 yes = true;
             }
 
-            // finally, get a normal ISECT HAE point.
+            // now get a normal ISECT HAE point.
             GeoPoint isectPoint;
             isectPoint.fromWorld( _terrain->getSRS()->getGeodeticSRS(), world );
-            s_mapLabel->setText( Stringify() << isectPoint.alt() );
+            s_mapLabel->setText( Stringify() << isectPoint.alt() << " m");
 
             // and move the marker.
             s_marker->setPosition(mapPoint);
+
+            // normal test.
+            osg::Quat q;
+            q.makeRotate(osg::Vec3(0,0,1), hits.begin()->getLocalIntersectNormal());
+            s_marker->setLocalRotation(q);
         }
 
         if (!yes)
@@ -161,8 +169,24 @@ struct QueryElevationHandler : public osgGA::GUIEventHandler
     const Map*       _map;
     const Terrain*   _terrain;
     bool             _mouseDown;
-    ElevationQuery   _query;
     osg::NodePath    _path;
+    osg::ref_ptr<ElevationEnvelope> _envelope;
+};
+
+
+struct ClickToRemoveElevation : public ControlEventHandler
+{
+    void onClick(Control*)
+    {
+        Map* map = s_mapNode->getMap();
+        ElevationLayerVector layers;
+        map->getLayers(layers);
+        map->beginUpdate();
+        for (ElevationLayerVector::iterator i = layers.begin(); i != layers.end(); ++i) {
+            map->removeLayer(i->get());
+        }
+        map->endUpdate();
+    }
 };
 
 
@@ -204,6 +228,7 @@ int main(int argc, char** argv)
     grid->setControl(0,r++,new LabelControl("Scene graph intersection:"));
     grid->setControl(0,r++,new LabelControl("EGM96 elevation:"));
     grid->setControl(0,r++,new LabelControl("Query resolution:"));
+    grid->setControl(0, r++, new ButtonControl("Click to remove all elevation data", new ClickToRemoveElevation()));
 
     r = 1;
     s_posLabel = grid->setControl(1,r++,new LabelControl(""));
@@ -214,11 +239,15 @@ int main(int argc, char** argv)
     s_egm96Label = grid->setControl(1,r++,new LabelControl(""));
     s_resLabel = grid->setControl(1,r++,new LabelControl(""));
 
-    s_marker = new PlaceNode();
-    s_marker->setMapNode( s_mapNode );
-    s_marker->setIconImage(osgDB::readImageFile("../data/placemark32.png"));
+    
+    Style markerStyle;
+    markerStyle.getOrCreate<ModelSymbol>()->url()->setLiteral("../data/axes.osgt.64.scale");
+    markerStyle.getOrCreate<ModelSymbol>()->autoScale() = true;
+    s_marker = new ModelNode(s_mapNode, markerStyle);
+    //s_marker->setMapNode( s_mapNode );
+    //s_marker->setIconImage(osgDB::readImageFile("../data/placemark32.png"));
     s_marker->setDynamic(true);
-    root->addChild( s_marker );
+    s_mapNode->addChild( s_marker );
 
     const SpatialReference* mapSRS = s_mapNode->getMapSRS();
     s_vdaLabel->setText( mapSRS->getVerticalDatum() ? 
diff --git a/src/applications/osgearth_ephemeris/osgearth_ephemeris.cpp b/src/applications/osgearth_ephemeris/osgearth_ephemeris.cpp
index 797a98c..c5d4dc4 100644
--- a/src/applications/osgearth_ephemeris/osgearth_ephemeris.cpp
+++ b/src/applications/osgearth_ephemeris/osgearth_ephemeris.cpp
@@ -22,6 +22,7 @@
 
 #include <osgViewer/Viewer>
 #include <osgEarth/Notify>
+#include <osgEarth/NodeUtils>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/Ephemeris>
@@ -76,7 +77,7 @@ main(int argc, char** argv)
 
     viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
 
-    osg::ref_ptr<osg::Image> mark = osgDB::readImageFile("../data/placemark32.png");
+    osg::ref_ptr<osg::Image> mark = osgDB::readRefImageFile("../data/placemark32.png");
     
     App app;
 
@@ -92,12 +93,12 @@ main(int argc, char** argv)
 
         app.sunPos = new PlaceNode(mapNode, GeoPoint(), mark.get(), "Sun");
         app.sunPos->setDynamic(true);
-        root->addChild( app.sunPos.get() );
+        mapNode->addChild( app.sunPos.get() );
 
         app.moonPos = new PlaceNode(mapNode, GeoPoint(), mark.get(), "Moon");
         app.moonPos->setDynamic(true);
 
-        root->addChild( app.moonPos.get() );        
+        mapNode->addChild( app.moonPos.get() );        
 
 
         app.sky = osgEarth::findTopMostNodeOfType<SkyNode>(node);        
diff --git a/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp b/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp
deleted file mode 100644
index 432e994..0000000
--- a/src/applications/osgearth_featureeditor/osgearth_featureeditor.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2016 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-* IN THE SOFTWARE.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osg/Notify>
-#include <osgGA/StateSetManipulator>
-#include <osgViewer/Viewer>
-#include <osgViewer/ViewerEventHandlers>
-#include <osgGA/GUIEventHandler>
-#include <osgEarth/Map>
-#include <osgEarth/MapNode>
-#include <osgEarthUtil/EarthManipulator>
-#include <osgEarthUtil/AutoClipPlaneHandler>
-#include <osgEarth/Utils>
-#include <osgEarthFeatures/GeometryUtils>
-
-#include <osgEarthSymbology/Style>
-
-#include <osgEarthDrivers/gdal/GDALOptions>
-#include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
-#include <osgEarthDrivers/agglite/AGGLiteOptions>
-#include <osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions>
-
-#include <osgEarthUtil/Controls>
-
-#include <osgEarthAnnotation/FeatureEditing>
-
-using namespace osgEarth;
-using namespace osgEarth::Features;
-using namespace osgEarth::Drivers;
-using namespace osgEarth::Symbology;
-using namespace osgEarth::Util;
-using namespace osgEarth::Util::Controls;
-using namespace osgEarth::Annotation;
-
-osg::Vec4
-randomColor()
-{
-    float r = (float)rand() / (float)RAND_MAX;
-    float g = (float)rand() / (float)RAND_MAX;
-    float b = (float)rand() / (float)RAND_MAX;
-    return osg::Vec4(r,g,b,1.0f);
-}
-
-
-static int s_fid = 0;
-
-static osg::ref_ptr< AddPointHandler > s_addPointHandler;
-static osg::ref_ptr< osg::Node > s_editor;
-static osg::ref_ptr< FeatureNode > s_featureNode;
-static osgViewer::Viewer* s_viewer;
-static osg::ref_ptr< osg::Group > s_root;
-static osg::ref_ptr< osg::Group > s_editorsRoot;
-static osg::ref_ptr< MapNode > s_mapNode;
-
-Grid* createToolBar()
-{    
-    Grid* toolbar = new Grid();
-    toolbar->setBackColor(0,0,0,0.5);
-    toolbar->setMargin( 10 );
-    toolbar->setPadding( 10 );
-    toolbar->setChildSpacing( 10 );
-    toolbar->setChildVertAlign( Control::ALIGN_CENTER );
-    toolbar->setAbsorbEvents( true );
-    toolbar->setVertAlign( Control::ALIGN_TOP );    
-    return toolbar;    
-}
-
-struct AddVertsModeHandler : public ControlEventHandler
-{
-    AddVertsModeHandler()        
-    {
-    }
-
-    void onClick( Control* control, int mouseButtonMask ) {
-
-        //remove the editor if it's valid
-        if (s_editor.valid())
-        {
-            s_editorsRoot->removeChild( s_editor.get() );
-            s_editor = NULL;
-
-            // Unset the stipple on the line
-            Style style = s_featureNode->getStyle();
-            style.get<LineSymbol>()->stroke()->stipple().unset();
-            s_featureNode->setStyle( style );            
-        }
-
-        //Add the new add point handler
-        if (!s_addPointHandler.valid())
-        {            
-            s_addPointHandler = new AddPointHandler( s_featureNode.get() );
-            s_addPointHandler->setIntersectionMask( 0x1 );
-            s_viewer->addEventHandler( s_addPointHandler.get() );
-        }        
-    }
-};
-
-struct EditModeHandler : public ControlEventHandler
-{
-    EditModeHandler()        
-    { 
-    }
-
-    void onClick( Control* control, int mouseButtonMask ) {
-        
-        //Remove the add point handler if it's valid
-        if (s_addPointHandler.valid())
-        {            
-            osgEarth::removeEventHandler( s_viewer, s_addPointHandler.get() );
-            s_addPointHandler = NULL;
-        }        
-
-        if (!s_editor.valid())
-        {            
-            Style style = s_featureNode->getStyle();
-            style.getOrCreate<LineSymbol>()->stroke()->stipple() = 0x00FF;                        
-            s_featureNode->setStyle( style );            
-            s_editor = new FeatureEditor( s_featureNode );
-            s_editorsRoot->addChild( s_editor.get() );            
-        }
-    }    
-};
-
-struct ChangeStyleHandler : public ControlEventHandler
-{
-    ChangeStyleHandler(const Style &style) 
-        : _style( style )
-    {
-        //nop
-    }
-
-    void onClick( Control* control, int mouseButtonMask ) {
-        s_featureNode->setStyle( _style );        
-    }
-
-    Style _style;  
-};
-
-Style buildStyle( const osg::Vec4 &color, float width )
-{
-    // Define a style for the feature data. Since we are going to render the
-    // vectors as lines, configure the line symbolizer:
-    Style style;
-
-    LineSymbol* ls = style.getOrCreateSymbol<LineSymbol>();
-    ls->stroke()->color() = color;
-    ls->stroke()->width() = width;        
-    
-    AltitudeSymbol* as = style.getOrCreate<AltitudeSymbol>();
-    as->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
-    as->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
-
-    style.getOrCreate<PolygonSymbol>()->fill()->color() = Color::Red;
-
-    RenderSymbol* rs = style.getOrCreateSymbol<RenderSymbol>();
-    rs->depthOffset()->enabled() = true;
-    rs->depthOffset()->minBias() = 1000;
-
-    return style;    
-}
-
-//
-// NOTE: run this sample from the repo/tests directory.
-//
-int main(int argc, char** argv)
-{
-    osg::ArgumentParser arguments(&argc,argv);
-
-    osgViewer::Viewer viewer(arguments);
-    s_viewer = &viewer;
-
-    // Start by creating the map:
-    s_mapNode = MapNode::load(arguments);
-    if ( !s_mapNode )
-    {
-        Map* map = new Map();
-
-        // Start with a basemap imagery layer; we'll be using the GDAL driver
-        // to load a local GeoTIFF file:
-        GDALOptions basemapOpt;
-        basemapOpt.url() = "../data/world.tif";
-        map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) );
-
-        // That's it, the map is ready; now create a MapNode to render the Map:
-        MapNodeOptions mapNodeOptions;
-        mapNodeOptions.enableLighting() = false;
-
-        s_mapNode = new MapNode( map, mapNodeOptions );
-    }
-    s_mapNode->setNodeMask( 0x01 );    
-
-        
-    // Define a style for the feature data.
-    Style style = buildStyle( Color::Yellow, 2.0f );    
-
-    //LineString* line = new LineString();    
-    Geometry* geom = GeometryUtils::geometryFromWKT("POLYGON((191.026667 87.63333,114.75 78,89.5 77.333336,81.833336 75.333336,70.683334 74.5,70.916664 73.666664,68.666664 73.666664,66.291664 71.505,57.65 71.166664,58 73.9,48.616665 73,49.198334 71.43,49.5 70.5,43.266666 68.666664,32.083332 71.5,32.083332 74,35 74,35 81,32 81,32 90,191.026667 87.63333))");
-    OE_NOTICE << "Geometry " << GeometryUtils::geometryToWKT(geom) << std::endl;
-    Feature* feature = new Feature(geom, s_mapNode->getMapSRS(), Style(), s_fid++);
-    s_featureNode = new FeatureNode( s_mapNode, feature );    
-    s_featureNode->setStyle( style );
-    
-    s_editorsRoot = new osg::Group;
-
-    s_root = new osg::Group;
-    s_root->addChild( s_mapNode.get() );
-    s_root->addChild( s_featureNode.get() );
-    s_root->addChild( s_editorsRoot.get() );
-
-
-    //Setup the controls
-    ControlCanvas* canvas = ControlCanvas::getOrCreate( &viewer );
-    s_root->addChild( canvas );
-    Grid *toolbar = createToolBar( );
-    canvas->addControl( toolbar );
-    canvas->setNodeMask( 0x1 << 1 );
-
-    int col = 0;
-    LabelControl* addVerts = new LabelControl("Add Verts");
-    toolbar->setControl(col++, 0, addVerts );    
-    addVerts->addEventHandler( new AddVertsModeHandler());
-    
-    LabelControl* edit = new LabelControl("Edit");
-    toolbar->setControl(col++, 0, edit );    
-    edit->addEventHandler(new EditModeHandler());
-
-    unsigned int row = 0;
-    Grid *styleBar = createToolBar( );
-    styleBar->setPosition(0, 50);
-    canvas->addControl( styleBar );
-    
-    //Make a list of styles
-    styleBar->setControl(0, row++, new LabelControl("Styles") );    
-
-    unsigned int numStyles = 8;
-    for (unsigned int i = 0; i < numStyles; ++i)
-    {
-        float w = 50;
-        osg::Vec4 color = randomColor();
-
-        float widths[3] = {2, 4, 8};
-
-        unsigned int r = row++;
-        for (unsigned int j = 0; j < 3; j++) 
-        {
-            Control* l = new Control();            
-            l->setBackColor( color );
-            l->addEventHandler(new ChangeStyleHandler(buildStyle( color, widths[j] ) ));
-            l->setSize(w,5 * widths[j]);
-            styleBar->setControl(j, r, l);
-        }
-    }
-   
-    
-    viewer.setSceneData( s_root.get() );
-    viewer.setCameraManipulator( new EarthManipulator() );
-
-    if ( s_mapNode )
-        viewer.getCamera()->addCullCallback( new osgEarth::Util::AutoClipPlaneCullCallback(s_mapNode) );
-
-    // add some stock OSG handlers:
-    viewer.addEventHandler(new osgViewer::StatsHandler());
-    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
-    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
-
-    return viewer.run();
-}
diff --git a/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp b/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
index 068c035..94e4521 100644
--- a/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
+++ b/src/applications/osgearth_featurefilter/osgearth_featurefilter.cpp
@@ -25,6 +25,7 @@
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthFeatures/Filter>
+#include <osgEarthFeatures/FilterContext>
 
 #define LC "[viewer] "
 
@@ -83,8 +84,6 @@ main(int argc, char** argv)
 {    
     //Run this example with the the feature_custom_filters.earth file in the tests directory for a simple example
     osg::ArgumentParser arguments(&argc,argv);
-    if ( arguments.read("--stencil") )
-        osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
 
     // create a viewer:
     osgViewer::Viewer viewer(arguments);
diff --git a/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp b/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
index db4fd2f..382d68b 100644
--- a/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
+++ b/src/applications/osgearth_featureinfo/osgearth_featureinfo.cpp
@@ -21,15 +21,15 @@
 */
 
 #include <osg/Notify>
-
 #include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 
+#include <osgEarthFeatures/GeometryUtils>
+#include <osgEarthFeatures/FeatureCursor>
+
 using namespace osgEarth::Features;
 using namespace osgEarth::Drivers;
 using namespace osgEarth::Symbology;
 
-#include <osgEarthFeatures/GeometryUtils>
-
 std::string attributeTypeToString( AttributeType type )
 {
     switch (type)
diff --git a/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp b/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp
deleted file mode 100644
index cd2cadf..0000000
--- a/src/applications/osgearth_featuremanip/osgearth_featuremanip.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2014 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osg/Notify>
-#include <osgGA/StateSetManipulator>
-#include <osgGA/GUIEventHandler>
-#include <osgViewer/Viewer>
-#include <osgViewer/ViewerEventHandlers>
-#include <osgEarth/MapNode>
-#include <osgEarthUtil/EarthManipulator>
-#include <osgEarthUtil/ExampleResources>
-#include <osgEarthUtil/Controls>
-
-#include <osgEarthUtil/FeatureManipTool>
-
-#define LC "[feature_manip] "
-
-using namespace osgEarth::Util;
-using namespace osgEarth::Util::Controls;
-
-//------------------------------------------------------------------------
-
-static FeatureManipTool* s_manipTool;
-
-static VBox* s_state_normal;
-static VBox* s_state_active;
-
-// Callback to toggle the visibility of the save/cancel buttons based on tool state
-struct ToggleUIStateCallback : public FeatureQueryTool::Callback
-{
-    // called when a valid feature is found under the mouse coords
-    virtual void onHit( FeatureSourceIndexNode* index, FeatureID fid, const EventArgs& args )
-    {
-        s_state_active->setVisible( true );
-    }
-
-    // called when no feature is found under the mouse coords
-    virtual void onMiss( const EventArgs& args )
-    {
-        s_state_active->setVisible( false );
-    }
-};
-
-
-// Cancels the manipulation when user clicks "cancel"
-struct OnCancel : public ControlEventHandler
-{
-    void onClick( Control* control )
-    {
-        s_manipTool->cancel();
-        s_state_active->setVisible( false );
-    }
-};
-
-
-// Commits the manipulation when user clicks "save"
-struct OnSave : public ControlEventHandler
-{
-    void onClick( Control* saveButton )
-    {
-        s_manipTool->commit();
-        s_state_active->setVisible( false );
-    }
-};
-
-
-// creaes a simple user interface for the manip demo
-Control*
-createUI()
-{
-    VBox* vbox = new VBox();
-    vbox->addControl( new LabelControl("Feature Manipulator Demo", Color::Yellow) );
-
-    s_state_normal = vbox->addControl(new VBox());
-    s_state_normal->addControl( new LabelControl("Shift-click on a feature to enter edit mode.") );
-    
-    s_state_active = vbox->addControl(new VBox());
-    s_state_active->setVisible( false );
-    s_state_active->addControl( new LabelControl("Drag the handles to position or rotation the feature.") );
-    
-    HBox* buttons = s_state_active->addControl(new HBox());
-    
-    LabelControl* cancel = buttons->addControl(new LabelControl("cancel"));
-    cancel->setBackColor(Color(Color::White,0.5));
-    cancel->setActiveColor(Color::Blue);
-    cancel->addEventHandler(new OnCancel());
-    cancel->setPadding( 5.0f );
-    cancel->setVertFill( true );
-
-    LabelControl* save = buttons->addControl(new LabelControl("save"));
-    save->setBackColor(Color(Color::White,0.5));
-    save->setActiveColor(Color::Blue);
-    save->addEventHandler(new OnSave());
-    save->setPadding( 5.0f );
-    save->setMargin(Control::SIDE_LEFT, 20.0f);
-    save->setVertFill( true );
-
-    vbox->setMargin( Control::SIDE_BOTTOM, 15.0f );
-    return vbox;
-} 
-
-//------------------------------------------------------------------------
-
-int
-main(int argc, char** argv)
-{
-    osg::ArgumentParser arguments(&argc,argv);
-    if ( arguments.read("--stencil") )
-        osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
-
-    // a basic OSG viewer
-    osgViewer::Viewer viewer(arguments);
-
-    // install our default manipulator (do this before using MapNodeHelper)
-    viewer.setCameraManipulator( new EarthManipulator() );
-
-    // load an earth file, and support all or our example command-line options
-    // and earth file <external> tags
-    osg::Group* root = MapNodeHelper().load( arguments, &viewer, createUI() );
-    if ( root )
-    {
-        viewer.setSceneData( root );
-
-        // configure the near/far so we don't clip things that are up close
-        viewer.getCamera()->setNearFarRatio(0.00002);
-
-        // add some stock OSG handlers:
-        viewer.addEventHandler(new osgViewer::StatsHandler());
-        viewer.addEventHandler(new osgViewer::WindowSizeHandler());
-        viewer.addEventHandler(new osgViewer::ThreadingHandler());
-        viewer.addEventHandler(new osgViewer::LODScaleHandler());
-        viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
-
-        MapNode* mapNode = MapNode::findMapNode( root );
-        if ( mapNode )
-        {
-            // install the Feature Manipulation tool.
-            s_manipTool = new FeatureManipTool( mapNode, true );
-            viewer.addEventHandler( s_manipTool );
-
-            s_manipTool->addCallback( new ToggleUIStateCallback() );
-        }
-
-        return viewer.run();
-    }
-    else
-    {
-        OE_NOTICE 
-            << "\nUsage: " << argv[0] << " file.earth" << std::endl
-            << MapNodeHelper().usage() << std::endl;
-    }
-}
diff --git a/src/applications/osgearth_featurequery/osgearth_featurequery.cpp b/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
index cc447f4..6d4522d 100644
--- a/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
+++ b/src/applications/osgearth_featurequery/osgearth_featurequery.cpp
@@ -24,19 +24,20 @@
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/MapNode>
-#include <osgEarth/ShaderLoader>
-#include <osgEarth/VirtualProgram>
 #include <osgEarth/Registry>
 #include <osgEarth/ObjectIndex>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/Controls>
-#include <osgEarthUtil/FeatureQueryTool>
+#include <osgEarthUtil/RTTPicker>
+#include <osgEarthFeatures/Feature>
+#include <osgEarthFeatures/FeatureIndex>
 
 #define LC "[feature_query] "
 
 using namespace osgEarth::Util;
 using namespace osgEarth::Util::Controls;
+using namespace osgEarth::Features;
 
 //-----------------------------------------------------------------------
 
@@ -60,19 +61,19 @@ Container* createUI()
  * user interface grid control.
  */
 
-class ReadoutCallback : public FeatureQueryTool::Callback
+class ReadoutCallback : public RTTPicker::Callback
 {
 public:
     ReadoutCallback(ControlCanvas* container) : _lastFID( ~0 )
     {
         _grid = new Grid();
-        _grid->setBackColor( Color(Color::Black,0.7f) );
+        _grid->setBackColor( osg::Vec4(0,0,0,0.7f) );
         container->addControl( _grid );
     }
 
     void onHit(ObjectID id)
     {
-        FeatureIndex* index = Registry::objectIndex()->get<FeatureIndex>( id );
+        FeatureIndex* index = Registry::objectIndex()->get<FeatureIndex>(id).get();
         Feature* feature = index ? index->getFeature( id ) : 0L;
         if ( feature && feature->getFID() != _lastFID )
         {
@@ -135,13 +136,13 @@ main(int argc, char** argv)
         if ( mapNode )
         {
             // Install the query tool.
-            FeatureQueryTool* tool = new FeatureQueryTool();
-            viewer.addEventHandler( tool );
-            tool->addChild( mapNode );
+            RTTPicker* picker = new RTTPicker();
+            viewer.addEventHandler( picker );
+            picker->addChild( mapNode );
 
             // Install a readout for feature metadata.
             ControlCanvas* canvas = ControlCanvas::getOrCreate(&viewer);
-            tool->setDefaultCallback( new ReadoutCallback(canvas) );
+            picker->setDefaultCallback( new ReadoutCallback(canvas) );
         }
 
         return viewer.run();
diff --git a/src/applications/osgearth_features/osgearth_features.cpp b/src/applications/osgearth_features/osgearth_features.cpp
index a5c10fb..51fd52d 100644
--- a/src/applications/osgearth_features/osgearth_features.cpp
+++ b/src/applications/osgearth_features/osgearth_features.cpp
@@ -26,13 +26,16 @@
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/Map>
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ModelLayer>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/AutoClipPlaneHandler>
 
 #include <osgEarthSymbology/Style>
-#include <osgEarthFeatures/ConvertTypeFilter>
+#include <osgEarthFeatures/FeatureModelLayer>
 
+#include <osgEarthDrivers/engine_rex/RexTerrainEngineOptions>
 #include <osgEarthDrivers/gdal/GDALOptions>
 #include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 #include <osgEarthDrivers/agglite/AGGLiteOptions>
@@ -82,16 +85,16 @@ int main(int argc, char** argv)
 
     // Start with a basemap imagery layer; we'll be using the GDAL driver
     // to load a local GeoTIFF file:
-    GDALOptions basemapOpt;
-    basemapOpt.url() = "../data/world.tif";
-    map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) );
-
+    GDALOptions basemap;
+    basemap.url() = "../data/world.tif";
+    map->addLayer( new ImageLayer(ImageLayerOptions("basemap", basemap)));
+    
     // Next we add a feature layer. 
-    OGRFeatureOptions featureOptions;
+    OGRFeatureOptions ogrData;
     if ( !useMem )
     {
         // Configures the feature driver to load the vectors from a shapefile:
-        featureOptions.url() = "../data/world.shp";
+        ogrData.url() = "../data/world.shp";
     }
     else
     {
@@ -101,9 +104,15 @@ int main(int argc, char** argv)
         line->push_back( osg::Vec3d(-120, 20, 0) );
         line->push_back( osg::Vec3d(-120, 60, 0) );
         line->push_back( osg::Vec3d(-60, 60, 0) );
-        featureOptions.geometry() = line;
+        ogrData.geometry() = line;
     }
 
+    // Make a feature source layer and add it to the Map:
+    FeatureSourceLayerOptions ogrLayer;
+    ogrLayer.name() = "vector-data";
+    ogrLayer.featureSource() = ogrData;
+    map->addLayer(new FeatureSourceLayer(ogrLayer));
+
     // Define a style for the feature data. Since we are going to render the
     // vectors as lines, configure the line symbolizer:
     Style style;
@@ -113,8 +122,11 @@ int main(int argc, char** argv)
     ls->stroke()->width() = 2.0f;
 
     // That's it, the map is ready; now create a MapNode to render the Map:
+    osgEarth::Drivers::RexTerrainEngine::RexTerrainEngineOptions rex;
+
     MapNodeOptions mapNodeOptions;
     mapNodeOptions.enableLighting() = false;
+    mapNodeOptions.setTerrainOptions(rex);
     MapNode* mapNode = new MapNode( map, mapNodeOptions );
 
     osg::Group* root = new osg::Group();
@@ -128,21 +140,21 @@ int main(int argc, char** argv)
     if (useRaster)
     {
         AGGLiteOptions rasterOptions;
-        rasterOptions.featureOptions() = featureOptions;
+        rasterOptions.featureOptions() = ogrData;
         rasterOptions.styles() = new StyleSheet();
         rasterOptions.styles()->addStyle( style );
-        map->addImageLayer( new ImageLayer("my features", rasterOptions) );
+        map->addLayer(new ImageLayer("My Features", rasterOptions) );
     }
     else //if (useGeom || useOverlay)
     {
-        FeatureGeomModelOptions geomOptions;
-        geomOptions.featureOptions() = featureOptions;
-        geomOptions.styles() = new StyleSheet();
-        geomOptions.styles()->addStyle( style );
-        geomOptions.enableLighting() = false;
-
-        ModelLayerOptions layerOptions( "my features", geomOptions );
-        map->addModelLayer( new ModelLayer(layerOptions) );
+        FeatureModelLayerOptions fml;
+        fml.name() = "My Features";
+        fml.featureSourceLayer() = "vector-data";
+        fml.styles() = new StyleSheet();
+        fml.styles()->addStyle(style);
+        fml.enableLighting() = false;
+
+        map->addLayer(new FeatureModelLayer(fml));
     }
 
     if ( useLabels )
@@ -155,19 +167,20 @@ int main(int argc, char** argv)
         TextSymbol* text = labelStyle.getOrCreateSymbol<TextSymbol>();
         text->content() = StringExpression( "[cntry_name]" );
         text->priority() = NumericExpression( "[pop_cntry]" );
-        text->removeDuplicateLabels() = true;
         text->size() = 16.0f;
         text->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
         text->fill()->color() = Color::White;
         text->halo()->color() = Color::DarkGray;
 
         // and configure a model layer:
-        FeatureGeomModelOptions geomOptions;
-        geomOptions.featureOptions() = featureOptions;
-        geomOptions.styles() = new StyleSheet();
-        geomOptions.styles()->addStyle( labelStyle );
-
-        map->addModelLayer( new ModelLayer("labels", geomOptions) );
+        FeatureModelLayerOptions fml;
+        fml.name() = "Labels";
+        fml.featureSourceLayer() = "vector-data";
+        //fml.featureSource() = featureOptions;
+        fml.styles() = new StyleSheet();
+        fml.styles()->addStyle( labelStyle );
+
+        map->addLayer(new FeatureModelLayer(fml));
     }
 
     
diff --git a/src/applications/osgearth_fog/osgearth_fog.cpp b/src/applications/osgearth_fog/osgearth_fog.cpp
deleted file mode 100644
index d85eeac..0000000
--- a/src/applications/osgearth_fog/osgearth_fog.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2016 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-* IN THE SOFTWARE.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osg/Notify>
-#include <osg/MatrixTransform>
-#include <osgGA/TrackballManipulator>
-#include <osgViewer/Viewer>
-#include <osgEarth/Registry>
-#include <osgDB/ReadFile>
-
-#include <osgEarthUtil/Fog>
-#include <osg/Fog>
-
-using namespace osgEarth;
-using namespace osgEarth::Util;
-
-int
-main(int argc, char** argv)
-{
-    osg::ArgumentParser arguments(&argc,argv);
-
-    // create a viewer:
-    osgViewer::Viewer viewer(arguments);
-
-    osg::Group* root = new osg::Group();    
-
-    // Setup a Fog state attribute    
-    osg::Fog* fog = new osg::Fog;            
-    fog->setColor( viewer.getCamera()->getClearColor() );                
-    fog->setDensity( 0.02 );    
-    fog->setMode(osg::Fog::LINEAR);
-    fog->setStart(5.0);
-    fog->setEnd(50.0);
-    root->getOrCreateStateSet()->setAttributeAndModes( fog, osg::StateAttribute::ON );                
-    
-    // Attach the FogCallback to keep the Fog uniforms up to date.
-    fog->setUpdateCallback(new FogCallback()); 
-
-    // Add the regular cow.
-    root->addChild(osgDB::readNodeFile("cow.osg"));
-
-    // Add a shader based cow to the right for comparison.
-    osg::MatrixTransform* mt = new osg::MatrixTransform;    
-    osg::Node* cowShader = osgDB::readNodeFile("cow.osg.10,0,0.trans");    
-    osgEarth::Registry::instance()->shaderGenerator().run(cowShader);    
-    
-    // Attach the fog effect so fog will take effect.
-    FogEffect* fogEffect = new FogEffect;
-    fogEffect->attach( cowShader->getOrCreateStateSet() );
-    mt->addChild( cowShader );
-    root->addChild(mt);
-
-    viewer.setCameraManipulator(new osgGA::TrackballManipulator());
-    
-    viewer.setSceneData( root );
-
-    while (!viewer.done())
-    {        
-        // Change fog modes ever 200 frames.
-        if (viewer.getFrameStamp()->getFrameNumber() % 200 == 0)
-        {
-            if (fog->getMode() == osg::Fog::LINEAR)
-            {
-                fog->setMode(osg::Fog::EXP);
-                OE_NOTICE << "switching to osg::Fog::EXP" << std::endl;
-            }
-            else if (fog->getMode() == osg::Fog::EXP)
-            {
-                fog->setMode(osg::Fog::EXP2);
-                OE_NOTICE << "switching to osg::Fog::EXP2" << std::endl;
-            }
-            else
-            {
-                fog->setMode(osg::Fog::LINEAR);
-                OE_NOTICE << "switching to osg::Fog::LINEAR" << std::endl;
-            }
-        }
-        viewer.frame();
-    }
-    return 0;
-}
diff --git a/src/applications/osgearth_graticule/osgearth_graticule.cpp b/src/applications/osgearth_graticule/osgearth_graticule.cpp
index 45164fd..eaa9901 100644
--- a/src/applications/osgearth_graticule/osgearth_graticule.cpp
+++ b/src/applications/osgearth_graticule/osgearth_graticule.cpp
@@ -34,9 +34,10 @@
 #include <osgEarthUtil/GeodeticGraticule>
 #include <osgEarthUtil/MGRSGraticule>
 #include <osgEarthUtil/UTMGraticule>
-#include <osgEarthUtil/GraticuleNode>
+#include <osgEarthUtil/GARSGraticule>
 
 using namespace osgEarth::Util;
+using namespace osgEarth::Annotation;
 
 int
 usage( const std::string& msg )
@@ -44,45 +45,15 @@ usage( const std::string& msg )
     OE_NOTICE 
         << msg << std::endl
         << "USAGE: osgearth_graticule [options] file.earth" << std::endl
-        << "   --geodetic            : display a geodetic (lat/long) graticule" << std::endl
+        << "   --geodetic            : display a Lat/Long graticule" << std::endl
         << "   --utm                 : display a UTM graticule" << std::endl
         << "   --mgrs                : display an MGRS graticule" << std::endl
-        << "   --shader              : display a geodetic graticule using the glsl shaders" << std::endl;
+        << "   --gars                : display a GARS graticule" << std::endl;
     return -1;
 }
 
 //------------------------------------------------------------------------
 
-struct ToggleGraticuleHandler : public ControlEventHandler
-{
-    ToggleGraticuleHandler( GraticuleNode* graticule ) : _graticule( graticule ) { }
-
-    void onValueChanged( Control* control, bool value )
-    {
-        _graticule->setVisible( value );
-    }
-
-    GraticuleNode* _graticule;
-};
-
-struct OffsetGraticuleHandler : public ControlEventHandler
-{
-    OffsetGraticuleHandler( GraticuleNode* graticule, const osg::Vec2f& offset ) :
-        _graticule( graticule ),
-        _offset(offset)
-    {
-        //nop
-    }
-
-    void onClick( Control* control, const osg::Vec2f& pos, int mouseButtonMask )
-    {
-        _graticule->setCenterOffset( _graticule->getCenterOffset() + _offset );
-    }
-
-    osg::Vec2f _offset;
-    GraticuleNode* _graticule;
-};
-
 int
 main(int argc, char** argv)
 {
@@ -93,8 +64,7 @@ main(int argc, char** argv)
     bool isUTM = arguments.read("--utm");
     bool isMGRS = arguments.read("--mgrs");
     bool isGeodetic = arguments.read("--geodetic");
-
-    bool isShader = !isUTM && !isMGRS && !isGeodetic;
+    bool isGARS = arguments.read("--gars");
 
     // load the .earth file from the command line.
     MapNode* mapNode = MapNode::load( arguments );
@@ -108,36 +78,31 @@ main(int argc, char** argv)
     osg::Group* root = new osg::Group();
     root->addChild( mapNode );
 
-    GraticuleNode* graticuleNode = 0;
-
     Formatter* formatter = 0L;
     if ( isUTM )
     {
-        UTMGraticule* gr = new UTMGraticule( mapNode );
-        root->addChild( gr );
+        UTMGraticule* gr = new UTMGraticule();
+        mapNode->getMap()->addLayer(gr);
         formatter = new MGRSFormatter();
     }
     else if ( isMGRS )
     {
-        MGRSGraticule* gr = new MGRSGraticule( mapNode );
-        root->addChild( gr );
+        MGRSGraticule* gr = new MGRSGraticule();
+        mapNode->getMap()->addLayer(gr);
         formatter = new MGRSFormatter();
     }
-    else if ( isGeodetic )
+    else if ( isGARS )
     {
-        GeodeticGraticule* gr = new GeodeticGraticule( mapNode );
-        GeodeticGraticuleOptions o = gr->getOptions();
-        o.lineStyle()->getOrCreate<LineSymbol>()->stroke()->color().set(1,0,0,1);
-        gr->setOptions( o );
-        root->addChild( gr );
+        GARSGraticule* gr = new GARSGraticule();
+        mapNode->getMap()->addLayer(gr);
         formatter = new LatLongFormatter();
     }
-    else
+    else // if ( isGeodetic )
     {
-        graticuleNode = new GraticuleNode( mapNode );
-        root->addChild( graticuleNode );
+        GeodeticGraticule* gr = new GeodeticGraticule();
+        mapNode->getMap()->addLayer(gr);
+        formatter = new LatLongFormatter();
     }
-
    
     // mouse coordinate readout:
     ControlCanvas* canvas = new ControlCanvas();
@@ -145,51 +110,9 @@ main(int argc, char** argv)
     VBox* vbox = new VBox();
     canvas->addControl( vbox );
 
-
     LabelControl* readout = new LabelControl();
     vbox->addControl( readout );
 
-    if (graticuleNode)
-    {
-        HBox* toggleBox = vbox->addControl( new HBox() );
-        toggleBox->setChildSpacing( 5 );
-        CheckBoxControl* toggleCheckBox = new CheckBoxControl( true );
-        toggleCheckBox->addEventHandler( new ToggleGraticuleHandler( graticuleNode ) );
-        toggleBox->addControl( toggleCheckBox );
-        LabelControl* labelControl = new LabelControl( "Show Graticule" );
-        labelControl->setFontSize( 24.0f );
-        toggleBox->addControl( labelControl  );
-
-        HBox* offsetBox = vbox->addControl( new HBox() );
-        offsetBox->setChildSpacing( 5 );
-        osg::Vec4 activeColor(1,.3,.3,1);
-
-        offsetBox->addControl(new LabelControl("Adjust Labels"));
-
-        double adj = 10.0;
-        LabelControl* left = new LabelControl("Left");
-        left->addEventHandler(new OffsetGraticuleHandler(graticuleNode, osg::Vec2f(-adj, 0.0)) );
-        offsetBox->addControl(left);
-        left->setActiveColor(activeColor);
-
-        LabelControl* right = new LabelControl("Right");
-        right->addEventHandler(new OffsetGraticuleHandler(graticuleNode, osg::Vec2f(adj, 0.0)) );
-        offsetBox->addControl(right);
-        right->setActiveColor(activeColor);
-
-        LabelControl* down = new LabelControl("Down");
-        down->addEventHandler(new OffsetGraticuleHandler(graticuleNode, osg::Vec2f(0.0, -adj)) );
-        offsetBox->addControl(down);
-        down->setActiveColor(activeColor);
-
-        LabelControl* up = new LabelControl("Up");
-        up->addEventHandler(new OffsetGraticuleHandler(graticuleNode, osg::Vec2f(0.0, adj)) );
-        offsetBox->addControl(up);
-        up->setActiveColor(activeColor);
-
-
-    }
-
     MouseCoordsTool* tool = new MouseCoordsTool( mapNode );
     tool->addCallback( new MouseCoordsLabelCallback(readout, formatter) );
     viewer.addEventHandler( tool );
diff --git a/src/applications/osgearth_horizon/osgearth_horizon.cpp b/src/applications/osgearth_horizon/osgearth_horizon.cpp
index 60f85bb..31702a2 100644
--- a/src/applications/osgearth_horizon/osgearth_horizon.cpp
+++ b/src/applications/osgearth_horizon/osgearth_horizon.cpp
@@ -45,7 +45,7 @@ int
 usage(const char* name)
 {
     OE_NOTICE 
-        << "\nUsage: " << name << " file.earth" << std::endl
+        << "\nUsage: " << name << " file.earth --activity" << std::endl
         << MapNodeHelper().usage() << std::endl;
 
     return 0;
@@ -111,6 +111,9 @@ main(int argc, char** argv)
     if ( arguments.read("--help") )
         return usage(argv[0]);
 
+    if (arguments.find("--activity") < 0)
+        return usage(argv[0]);
+
     // create a viewer:
     osgViewer::Viewer viewer(arguments);
 
diff --git a/src/applications/osgearth_fog/CMakeLists.txt b/src/applications/osgearth_htm/CMakeLists.txt
similarity index 72%
rename from src/applications/osgearth_fog/CMakeLists.txt
rename to src/applications/osgearth_htm/CMakeLists.txt
index 7e66e05..5b91143 100644
--- a/src/applications/osgearth_fog/CMakeLists.txt
+++ b/src/applications/osgearth_htm/CMakeLists.txt
@@ -1,7 +1,7 @@
 INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
 SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
 
-SET(TARGET_SRC osgearth_fog.cpp )
+SET(TARGET_SRC osgearth_htm.cpp )
 
 #### end var setup  ###
-SETUP_APPLICATION(osgearth_fog)
\ No newline at end of file
+SETUP_APPLICATION(osgearth_htm)
\ No newline at end of file
diff --git a/src/applications/osgearth_viewer/osgearth_viewer.cpp b/src/applications/osgearth_htm/osgearth_htm.cpp
similarity index 52%
copy from src/applications/osgearth_viewer/osgearth_viewer.cpp
copy to src/applications/osgearth_htm/osgearth_htm.cpp
index 2e4b118..2ed06c8 100644
--- a/src/applications/osgearth_viewer/osgearth_viewer.cpp
+++ b/src/applications/osgearth_htm/osgearth_htm.cpp
@@ -24,74 +24,95 @@
 #include <osgEarth/Notify>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
-
-#include <osgEarth/Cache>
-#include <osgEarthDrivers/cache_filesystem/FileSystemCache>
+#include <osgEarth/MapNode>
+#include <osgEarth/ThreadingUtils>
+#include <osgEarth/Metrics>
+#include <iostream>
+#include <osgEarthUtil/HTM>
+#include <osgEarthAnnotation/PlaceNode>
+#include <osgEarth/Random>
 
 #define LC "[viewer] "
 
 using namespace osgEarth;
 using namespace osgEarth::Util;
+using namespace osgEarth::Annotation;
 
 int
-usage(const char* name)
+usage(const char* name, const char* msg =NULL)
 {
     OE_NOTICE 
-        << "\nUsage: " << name << " file.earth" << std::endl
+        << (msg ? msg : "")
+        << "\nUsage: " << name << " file.earth --model <file> [--num <number>] [--debug]" << std::endl
         << MapNodeHelper().usage() << std::endl;
 
     return 0;
 }
 
+
 int
 main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
 
-    // help?
     if ( arguments.read("--help") )
         return usage(argv[0]);
 
-    float vfov = -1.0f;
-    arguments.read("--vfov", vfov);
-
-    // create a viewer:
+    // Viewer setup
     osgViewer::Viewer viewer(arguments);
-
-    // Tell the database pager to not modify the unref settings
     viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( true, false );
-
-    // thread-safe initialization of the OSG wrapper manager. Calling this here
-    // prevents the "unsupported wrapper" messages from OSG
     osgDB::Registry::instance()->getObjectWrapperManager()->findWrapper("osg::Image");
-
-    // install our default manipulator (do this before calling load)
     viewer.setCameraManipulator( new EarthManipulator(arguments) );
-
-    // disable the small-feature culling
     viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
-
-    // set a near/far ratio that is smaller than the default. This allows us to get
-    // closer to the ground without near clipping. If you need more, use --logdepth
     viewer.getCamera()->setNearFarRatio(0.0001);
 
-    if ( vfov > 0.0 )
-    {
-        double fov, ar, n, f;
-        viewer.getCamera()->getProjectionMatrixAsPerspective(fov, ar, n, f);
-        viewer.getCamera()->setProjectionMatrixAsPerspective(vfov, ar, n, f);
-    }
+    std::string modelPath;
+    if (arguments.read("--model", modelPath) == false)
+        return usage(argv[0]);
 
-    // load an earth file, and support all or our example command-line options
-    // and earth file <external> tags    
+    osg::ref_ptr<osg::Node> model = osgDB::readRefNodeFile(modelPath);
+    if (model.valid() == false)
+        return usage(argv[0], "Cannot load model file");
+
+    int numObjects = 10000;
+    arguments.read("--num", numObjects);
+
+    bool debug = arguments.read("--debug");
+
+    // Load the earth file
     osg::Node* node = MapNodeHelper().load(arguments, &viewer);
-    if ( node )
+    if (!node)
+        return usage(argv[0], "Cannot load earth file");
+
+    viewer.setSceneData( node );
+    MapNode* mapNode = MapNode::get(node);
+
+    // Randomly place object instances around the US.
+    const SpatialReference* wgs84 = SpatialReference::get("wgs84");
+    Random prng;
+    HTMGroup* htm = new HTMGroup();
+    htm->setMaximumObjectsPerCell(250);
+    htm->setMaximumCellSize(500000);
+    htm->setMinimumCellSize(25000);
+    htm->setRangeFactor(5);
+    mapNode->addChild(htm);
+        
+    for (unsigned i = 0; i < numObjects; ++i)
     {
-        viewer.setSceneData( node );
-        viewer.run();
-    }
-    else
-    {
-        return usage(argv[0]);
+        GeoTransform* xform = new GeoTransform();
+        xform->addChild(model.get());
+
+        double lon = -115 + prng.next() * 40;
+        double lat = 25 + prng.next() * 25;
+
+        xform->setPosition(GeoPoint(wgs84, lon, lat, 0, ALTMODE_ABSOLUTE));
+
+        htm->addChild(xform);
+
+        if (i%1000 == 0)
+            std::cout << "\r" << i << "/" << numObjects << std::flush;
     }
+    std::cout << std::endl;
+
+    return viewer.run();
 }
diff --git a/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp b/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
index 8435073..9d25134 100644
--- a/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
+++ b/src/applications/osgearth_imageoverlay/osgearth_imageoverlay.cpp
@@ -31,6 +31,7 @@
 #include <osgEarthUtil/Controls>
 #include <osgEarth/Utils>
 #include <osgEarth/VirtualProgram>
+#include <osgEarth/FileUtils>
 
 #include <osg/ImageStream>
 #include <osgDB/FileNameUtils>
@@ -196,8 +197,8 @@ main(int argc, char** argv)
     bool moveVert = arguments.read("--vert");
 
     // load the .earth file from the command line.
-    osg::Node* earthNode = osgDB::readNodeFiles( arguments );
-    if (!earthNode)
+    osg::ref_ptr<osg::Node> earthNode = osgDB::readNodeFiles( arguments );
+    if (!earthNode.valid())
         return usage( "Unable to load earth model." );
 
     osgViewer::Viewer viewer(arguments);
@@ -206,14 +207,14 @@ main(int argc, char** argv)
     viewer.setCameraManipulator( manip );
 
     osg::Group* root = new osg::Group();
-    root->addChild( earthNode );
+    root->addChild( earthNode.get() );
 
     //Create the control panel
     root->addChild( createControlPanel(&viewer) );
 
     viewer.setSceneData( root );
     
-    osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode );
+    osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode.get() );
     if ( mapNode )
     {
 
@@ -221,10 +222,10 @@ main(int argc, char** argv)
         {
             std::string imageFile = imageFiles[i];
             //Read the image file and play it if it's a movie
-            osg::Image* image = osgDB::readImageFile(imageFile);
-            if (image)
+            osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(imageFile);
+            if (image.valid())
             {
-                osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(image);
+                osg::ImageStream* is = dynamic_cast<osg::ImageStream*>(image.get());
                 if (is)
                 {
                     is->play();
@@ -234,19 +235,19 @@ main(int argc, char** argv)
             //Create a new ImageOverlay and set it's bounds
             //ImageOverlay* overlay = new ImageOverlay(mapNode->getMap()->getProfile()->getSRS()->getEllipsoid(), image);        
             ImageOverlay* overlay = new ImageOverlay(mapNode);
-            overlay->setImage( image );
+            overlay->setImage( image.get() );
             overlay->setBounds(imageBounds[i]);
             
-            root->addChild( overlay );
+            mapNode->addChild( overlay );
 
 
             //Create a new ImageOverlayEditor and set it's node mask to 0 to hide it initially
             osg::Node* editor = new ImageOverlayEditor( overlay, moveVert);
             editor->setNodeMask( 0 );
-            root->addChild( editor );      
+            mapNode->addChild( editor );      
             
             // Add an image preview
-            ImageControl* imageCon = new ImageControl( image );
+            ImageControl* imageCon = new ImageControl( image.get() );
             imageCon->setSize( 64, 64 );
             imageCon->setVertAlign( Control::ALIGN_CENTER );
             s_layerBox->setControl( 0, i, imageCon );            
diff --git a/src/applications/osgearth_featureeditor/CMakeLists.txt b/src/applications/osgearth_infinitescroll/CMakeLists.txt
similarity index 66%
copy from src/applications/osgearth_featureeditor/CMakeLists.txt
copy to src/applications/osgearth_infinitescroll/CMakeLists.txt
index b35afd6..cdf4228 100644
--- a/src/applications/osgearth_featureeditor/CMakeLists.txt
+++ b/src/applications/osgearth_infinitescroll/CMakeLists.txt
@@ -1,8 +1,7 @@
 INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
-
 SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
 
-SET(TARGET_SRC osgearth_featureeditor.cpp )
+SET(TARGET_SRC osgearth_infinitescroll.cpp )
 
 #### end var setup  ###
-SETUP_APPLICATION(osgearth_featureeditor)
\ No newline at end of file
+SETUP_APPLICATION(osgearth_infinitescroll)
\ No newline at end of file
diff --git a/src/applications/osgearth_infinitescroll/osgearth_infinitescroll.cpp b/src/applications/osgearth_infinitescroll/osgearth_infinitescroll.cpp
new file mode 100644
index 0000000..4bce3f6
--- /dev/null
+++ b/src/applications/osgearth_infinitescroll/osgearth_infinitescroll.cpp
@@ -0,0 +1,207 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2016 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+* IN THE SOFTWARE.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+#include <osgViewer/Viewer>
+#include <osgEarth/Notify>
+#include <osgEarthUtil/EarthManipulator>
+#include <osgEarthUtil/ExampleResources>
+#include <osgEarth/MapNode>
+#include <osgEarth/ThreadingUtils>
+#include <osgEarth/Metrics>
+#include <iostream>
+
+#define LC "[viewer] "
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+
+int
+usage(const char* name)
+{
+    OE_NOTICE
+        << "\nUsage: " << name << " file.earth" << std::endl
+        << MapNodeHelper().usage() << std::endl;
+
+    return 0;
+}
+
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser arguments(&argc,argv);
+
+    // help?
+    if ( arguments.read("--help") )
+        return usage(argv[0]);
+
+    float vfov = -1.0f;
+    arguments.read("--vfov", vfov);
+
+
+
+    // create a viewer:
+    osgViewer::Viewer viewer(arguments);
+
+    // Tell the database pager to not modify the unref settings
+    viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( true, false );
+
+    // thread-safe initialization of the OSG wrapper manager. Calling this here
+    // prevents the "unsupported wrapper" messages from OSG
+    osgDB::Registry::instance()->getObjectWrapperManager()->findWrapper("osg::Image");
+
+    // install our default manipulator (do this before calling load)
+    osg::ref_ptr< EarthManipulator > manipulator = new EarthManipulator(arguments);
+    viewer.setCameraManipulator( manipulator );
+
+    // disable the small-feature culling
+    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
+
+    // set a near/far ratio that is smaller than the default. This allows us to get
+    // closer to the ground without near clipping. If you need more, use --logdepth
+    viewer.getCamera()->setNearFarRatio(0.0001);
+
+    if ( vfov > 0.0 )
+    {
+        double fov, ar, n, f;
+        viewer.getCamera()->getProjectionMatrixAsPerspective(fov, ar, n, f);
+        viewer.getCamera()->setProjectionMatrixAsPerspective(vfov, ar, n, f);
+    }
+
+    // load an earth file, and support all or our example command-line options
+    // and earth file <external> tags
+    osg::ref_ptr< osg::Node > node = MapNodeHelper().load(arguments, &viewer);
+
+    osg::ref_ptr< MapNode > mapNode = MapNode::findMapNode(node.get());
+
+    if ( mapNode.valid() )
+    {
+        if (mapNode->isGeocentric())
+        {
+            OE_NOTICE << "Please run this example with a projected earth file" << std::endl;
+            return 1;
+        }
+        GeoExtent mapExtent = mapNode->getMap()->getProfile()->getExtent();
+
+        //Disable the middle mouse by default, which is rotate.  This will keep us in 2D mode.
+        manipulator->getSettings()->bindMouse(osgEarth::Util::EarthManipulator::ACTION_NULL, osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON, 0);
+
+        // Compute a sensible max range so that the user can't zoom out too far to where we need more than 3 transforms.
+        double maxDim = osg::maximum(mapExtent.width(), mapExtent.height());
+        double range = ((0.5 * maxDim) / 0.267949849);
+        manipulator->getSettings()->setMinMaxDistance(0.0, range);
+
+        osg::Group* root = new osg::Group;
+
+        // We're going to draw the map three times so that we can provide an infinite view scrolling left to right.
+        
+        // The centerMatrix is centered around the eye point.
+        osg::MatrixTransform* centerMatrix = new osg::MatrixTransform;
+        centerMatrix->addChild( mapNode );
+        root->addChild( centerMatrix );
+
+
+        // The left matrix is to the left of the center matrix
+        osg::MatrixTransform* leftMatrix = new osg::MatrixTransform;
+        leftMatrix->addChild( mapNode );
+        root->addChild( leftMatrix );
+
+        // The right matrix is to the right of the center matrix
+        osg::MatrixTransform* rightMatrix = new osg::MatrixTransform;
+        rightMatrix->addChild( mapNode );
+        root->addChild( rightMatrix );
+
+        viewer.setSceneData( root );
+
+        while (!viewer.done())
+        {
+            // Get the current viewpoint from the EarthManipulator
+            Viewpoint vp = manipulator->getViewpoint();
+            double eyeX = vp.focalPoint()->x();
+
+            GeoPoint focalPoint = *vp.focalPoint();
+
+            // Adjust the focal point if the user is trying to too far north or south.
+            if (focalPoint.y() > mapExtent.yMax())
+            {
+                focalPoint.y() = mapExtent.yMax();
+                vp.focalPoint() = focalPoint;
+                manipulator->setViewpoint( vp );
+            }
+            else if (focalPoint.y() < mapExtent.yMin())
+            {
+                focalPoint.y() = mapExtent.yMin();
+                vp.focalPoint() = focalPoint;
+                manipulator->setViewpoint( vp );
+            }
+                                    
+            GeoExtent centerExtent =  mapExtent;
+            
+            // Figure out which direction we need to shift the map extent 
+            float direction = 0.0;
+            if (eyeX < mapExtent.xMin())
+            {
+                // Move to the left
+                direction = -1.0;
+            }
+            else if (eyeX > mapExtent.xMax())
+            {
+                // Move to the right
+                direction = 1.0;
+            }
+
+
+            // Shift the center extent so that it's centered around the eye point.
+            float offset = 0.0;
+
+            if (direction != 0.0)
+            {
+                while (true)
+                {
+                    centerExtent = GeoExtent(centerExtent.getSRS(),
+                                   mapExtent.xMin() + offset, mapExtent.yMin(), 
+                                   mapExtent.xMax() + offset, mapExtent.yMax());
+                    if (eyeX >= centerExtent.xMin() && eyeX <= centerExtent.xMax())
+                    {
+                        break;
+                    }
+
+                    offset += direction * centerExtent.width();
+                }
+            }
+
+            // Update the matrix transforms.
+            centerMatrix->setMatrix(osg::Matrixd::translate(offset, 0.0, 0.0));
+            leftMatrix->setMatrix(osg::Matrixd::translate(offset - mapExtent.width(), 0.0, 0.0));
+            rightMatrix->setMatrix(osg::Matrixd::translate(offset + mapExtent.width(), 0.0, 0.0));
+          
+            viewer.frame();
+        }
+        
+    }
+    else
+    {
+        return usage(argv[0]);
+    }
+
+    return 0;
+}
diff --git a/src/applications/osgearth_featuremanip/CMakeLists.txt b/src/applications/osgearth_lights/CMakeLists.txt
similarity index 67%
rename from src/applications/osgearth_featuremanip/CMakeLists.txt
rename to src/applications/osgearth_lights/CMakeLists.txt
index 7627241..6574558 100644
--- a/src/applications/osgearth_featuremanip/CMakeLists.txt
+++ b/src/applications/osgearth_lights/CMakeLists.txt
@@ -1,7 +1,7 @@
 INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
 SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
 
-SET(TARGET_SRC osgearth_featuremanip.cpp )
+SET(TARGET_SRC osgearth_lights.cpp )
 
 #### end var setup  ###
-SETUP_APPLICATION(osgearth_featuremanip)
\ No newline at end of file
+SETUP_APPLICATION(osgearth_lights)
\ No newline at end of file
diff --git a/src/applications/osgearth_lights/osgearth_lights.cpp b/src/applications/osgearth_lights/osgearth_lights.cpp
new file mode 100644
index 0000000..7acf6d9
--- /dev/null
+++ b/src/applications/osgearth_lights/osgearth_lights.cpp
@@ -0,0 +1,244 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+* Copyright 2015 Pelican Mapping
+* http://osgearth.org
+*
+* osgEarth is free software; you can redistribute it and/or modify
+* it under the terms of the GNU Lesser General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+* IN THE SOFTWARE.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * Lights test. This application is for testing the LightSource support in osgEarth.
+ */
+#include <osgViewer/Viewer>
+#include <osgEarth/Notify>
+#include <osgEarth/Lighting>
+#include <osgEarth/PhongLightingEffect>
+#include <osgEarth/NodeUtils>
+#include <osgEarthUtil/EarthManipulator>
+#include <osgEarthUtil/ExampleResources>
+#include <osgEarthUtil/Ephemeris>
+#include <osgEarthUtil/Shadowing>
+
+#define LC "[lights] "
+
+using namespace osgEarth;
+using namespace osgEarth::Util;
+
+int
+usage(const char* name)
+{
+    OE_NOTICE 
+        << "\nUsage: " << name << " file.earth" << std::endl
+        << MapNodeHelper().usage() << std::endl;
+
+    return 0;
+}
+
+// converts a double-precision Vec3d to an equivalent single-precision Vec4f position
+// as needed for light positions.
+osg::Vec4
+worldToVec4(const osg::Vec3d& ecef)
+{
+    osg::Vec4 result(0.0f, 0.0f, 0.0f, 1.0f);
+    osg::Vec3d d = ecef;
+    while (d.length() > 1e6)
+    {
+        d *= 0.1;
+        result.w() *= 0.1;
+    }
+    return osg::Vec4(d.x(), d.y(), d.z(), result.w());
+}
+
+osg::Vec4
+randomColor()
+{
+    float r = (float)rand() / (float)RAND_MAX;
+    float g = (float)rand() / (float)RAND_MAX;
+    float b = (float)rand() / (float)RAND_MAX;
+    return osg::Vec4(r,g,b,1.0f);
+}
+
+
+osg::Group*
+addLights(osg::View* view, osg::Node* root, int lightNum)
+{
+    MapNode* mapNode = MapNode::get(root);
+    const SpatialReference* mapsrs = mapNode->getMapSRS();
+    const SpatialReference* geosrs = mapsrs->getGeographicSRS();
+    
+    osg::Vec3d world;
+    osg::Group* lights = new osg::Group();
+
+    // Add a directional light that simulates the sun - but skip this if a sky
+    // was already added in the earth file.
+    if (lightNum == 0)
+    {
+        Ephemeris e;
+        DateTime dt(2016, 8, 10, 14.0);
+        world = e.getSunPositionECEF(dt);
+
+        osg::Light* sun = new osg::Light(lightNum++);
+        world.normalize();
+        sun->setPosition(osg::Vec4d(world, 0.0));
+
+        sun->setAmbient(osg::Vec4(0.2, 0.2, 0.2, 1.0));
+        sun->setDiffuse(osg::Vec4(1.0, 1.0, 0.9, 1.0));
+
+        osg::LightSource* sunLS = new osg::LightSource();
+        sunLS->setLight(sun);
+
+        lights->addChild( sunLS );
+
+        ShadowCaster* caster = osgEarth::findTopMostNodeOfType<ShadowCaster>(root);
+        if (caster)
+        {
+            OE_INFO << "Found a shadow caster!\n";
+            caster->setLight(sun);
+        }
+    }
+
+#if 1
+    // A red spot light. A spot light has a real position in space 
+    // and points in a specific direciton. The Cutoff and Exponent
+    // properties control the cone angle and sharpness, respectively
+    {
+        GeoPoint p(geosrs, -121, 34, 5000000., ALTMODE_ABSOLUTE);
+        p.toWorld(world);
+
+        osg::Light* spot = new osg::Light(lightNum++);    
+        spot->setPosition(worldToVec4(world));
+        spot->setAmbient(osg::Vec4(0,0.2,0,1));
+        spot->setDiffuse(osg::Vec4(1,0,0,1));
+        spot->setSpotCutoff(20.0f);
+        spot->setSpotExponent(100.0f);
+
+        // point straight down at the map:
+        world.normalize();
+        spot->setDirection(-world);
+
+        osg::LightSource* spotLS = new osg::LightSource();
+        spotLS->setLight(spot);
+
+        lights->addChild( spotLS );
+    }
+
+    // A green point light. A Point light lives at a real location in 
+    // space and lights equally in all directions.
+    {
+        GeoPoint p(geosrs, -45, -35, 1000000., ALTMODE_ABSOLUTE);
+        p.toWorld(world);
+
+        osg::Light* point = new osg::Light(lightNum++);
+        point->setPosition(worldToVec4(world));
+        point->setAmbient(osg::Vec4(0,0,0,1));
+        point->setDiffuse(osg::Vec4(1.0, 1.0, 0.0,1));
+
+        osg::LightSource* pointLS = new osg::LightSource();
+        pointLS->setLight(point);
+
+        lights->addChild( pointLS );
+    }
+#endif
+
+    // Generate the necessary uniforms for the shaders.
+    GenerateGL3LightingUniforms gen;
+    lights->accept(gen);
+
+    return lights;
+}
+
+
+
+int
+main(int argc, char** argv)
+{
+    osg::ArgumentParser arguments(&argc,argv);
+
+    // help?
+    if ( arguments.read("--help") )
+        return usage(argv[0]);
+
+    // create a viewer:
+    osgViewer::Viewer viewer(arguments);
+
+    // Whether to test updating material
+    bool update = arguments.read("--update");
+
+    // Tell the database pager to not modify the unref settings
+    viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( true, false );
+
+    // install our default manipulator (do this before calling load)
+    viewer.setCameraManipulator( new EarthManipulator(arguments) );
+
+    // disable the small-feature culling
+    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
+
+    viewer.setLightingMode(viewer.NO_LIGHT);
+
+    // load an earth file, and support all or our example command-line options
+    osg::ref_ptr<osg::Node> node = MapNodeHelper().load(arguments, &viewer);
+    if (node.valid())
+    {
+        MapNode* mapNode = MapNode::get(node.get());
+        if ( !mapNode )
+            return -1;
+        
+        // Example of a custom material for the terrain.
+        osg::ref_ptr< osg::Material > material = 0;
+        if (update)
+        {
+            OE_NOTICE << "Custom material" << std::endl;
+            material = new osg::Material;
+            material->setDiffuse(osg::Material::FRONT, osg::Vec4(1,1,1,1));        
+            material->setAmbient(osg::Material::FRONT, osg::Vec4(1,1,1,1));
+            // Attach our StateAttributeCallback so that uniforms are updated.
+            material->setUpdateCallback(new MaterialCallback());
+            mapNode->getOrCreateStateSet()->setAttributeAndModes(material);
+        }
+
+        // Does a Sky already exist (loaded from the earth file)?
+        SkyNode* sky = osgEarth::findTopMostNodeOfType<SkyNode>(node.get());
+        if (!sky)
+        {
+            // Add phong lighting.
+            PhongLightingEffect* phong = new PhongLightingEffect();
+            phong->attach(node->getOrCreateStateSet());
+        }
+
+        osg::Group* lights = addLights(&viewer, node.get(), sky?1:0);
+
+        mapNode->addChild(lights);
+        
+        viewer.setSceneData(node.get()); 
+        while (!viewer.done())
+        {         
+            if (viewer.getFrameStamp()->getFrameNumber() % 100 == 0)
+            {
+                if (material)
+                {
+                    material->setDiffuse(osg::Material::FRONT, randomColor());
+                }
+            }
+            viewer.frame();
+        }
+        return 0;
+    }
+    else
+    {
+        return usage(argv[0]);
+    }
+}
diff --git a/src/applications/osgearth_los/osgearth_los.cpp b/src/applications/osgearth_los/osgearth_los.cpp
index 5373f3d..87c3f80 100644
--- a/src/applications/osgearth_los/osgearth_los.cpp
+++ b/src/applications/osgearth_los/osgearth_los.cpp
@@ -34,6 +34,8 @@
 #include <osg/io_utils>
 #include <osg/MatrixTransform>
 #include <osg/Depth>
+#include <osgEarth/TerrainTileNode>
+#include <osgEarth/FileUtils>
 
 using namespace osgEarth;
 using namespace osgEarth::Util;
@@ -103,6 +105,35 @@ osg::Node* createPlane(osg::Node* node, const GeoPoint& pos, const SpatialRefere
     return positioner;
 }
 
+class CacheExtentNodeVisitor : public osg::NodeVisitor
+{
+public:
+    CacheExtentNodeVisitor(GeoExtent& extent):
+      osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
+          _extent(extent)
+      {
+      }
+
+      void apply(osg::Node& node)
+      {
+          TerrainTileNode* tile = dynamic_cast<TerrainTileNode*>(&node);
+          if (tile && tile->getKey().valid())
+          {              
+              if (tile->getKey().getExtent().intersects(_extent) && tile->getKey().getLevelOfDetail() < 11)
+              {
+                  // Set this tile to not expire.
+                  tile->setMinimumExpirationTime(DBL_MAX);
+                  OE_NOTICE << "Preloading children for " << tile->getKey().str() << std::endl;
+                  tile->loadChildren();
+              }
+          }          
+          traverse(node);
+      }
+
+      GeoExtent _extent;
+};
+
+
 int
 main(int argc, char** argv)
 {
@@ -110,8 +141,8 @@ main(int argc, char** argv)
     osgViewer::Viewer viewer(arguments);
 
     // load the .earth file from the command line.
-    osg::Node* earthNode = osgDB::readNodeFiles( arguments );
-    if (!earthNode)
+    osg::ref_ptr<osg::Node> earthNode = osgDB::readNodeFiles( arguments );
+    if (!earthNode.valid())
     {
         OE_NOTICE << "Unable to load earth model" << std::endl;
         return 1;
@@ -119,7 +150,7 @@ main(int argc, char** argv)
 
     osg::Group* root = new osg::Group();
 
-    osgEarth::MapNode * mapNode = osgEarth::MapNode::findMapNode( earthNode );
+    osgEarth::MapNode * mapNode = osgEarth::MapNode::findMapNode( earthNode.get() );
     if (!mapNode)
     {
         OE_NOTICE << "Could not find MapNode " << std::endl;
@@ -184,11 +215,11 @@ main(int argc, char** argv)
     losGroup->addChild( radialRelEditor );
 
     //Load a plane model.  
-    osg::ref_ptr< osg::Node >  plane = osgDB::readNodeFile("../data/cessna.osgb.5,5,5.scale");
+    osg::ref_ptr< osg::Node >  plane = osgDB::readRefNodeFile("../data/cessna.osgb.5,5,5.scale");
 
     //Create 2 moving planes
-    osg::Node* plane1 = createPlane(plane, GeoPoint(geoSRS, -121.656, 46.0935, 4133.06, ALTMODE_ABSOLUTE), mapSRS, 5000, 20);
-    osg::Node* plane2 = createPlane(plane, GeoPoint(geoSRS, -121.321, 46.2589, 1390.09, ALTMODE_ABSOLUTE), mapSRS, 3000, 5);
+    osg::Node* plane1 = createPlane(plane.get(), GeoPoint(geoSRS, -121.656, 46.0935, 4133.06, ALTMODE_ABSOLUTE), mapSRS, 5000, 20);
+    osg::Node* plane2 = createPlane(plane.get(), GeoPoint(geoSRS, -121.321, 46.2589, 1390.09, ALTMODE_ABSOLUTE), mapSRS, 3000, 5);
     root->addChild( plane1 );
     root->addChild( plane2 );
 
@@ -199,7 +230,7 @@ main(int argc, char** argv)
     tetheredLOS->setUpdateCallback( new LineOfSightTether( plane1, plane2 ) );
 
     //Create another plane and attach a RadialLineOfSightNode to it using the RadialLineOfSightTether
-    osg::Node* plane3 = createPlane(plane, GeoPoint(geoSRS, -121.463, 46.3548, 1348.71, ALTMODE_ABSOLUTE), mapSRS, 10000, 5);
+    osg::Node* plane3 = createPlane(plane.get(), GeoPoint(geoSRS, -121.463, 46.3548, 1348.71, ALTMODE_ABSOLUTE), mapSRS, 10000, 5);
     losGroup->addChild( plane3 );
     RadialLineOfSightNode* tetheredRadial = new RadialLineOfSightNode( mapNode );
     tetheredRadial->setRadius( 5000 );
@@ -229,5 +260,21 @@ main(int argc, char** argv)
     viewer.addEventHandler(new osgViewer::LODScaleHandler());
     viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
 
-    return viewer.run();
+    /*
+    TerrainTileNodeVisitor v;
+    root->accept(v);
+    */
+
+    GeoPoint center(geoSRS, -121.656, 46.0935, 4133.06, ALTMODE_ABSOLUTE);
+
+    GeoExtent extent(geoSRS, center.x() - 0.5, center.y() - 0.5, center.x() + 0.5, center.y() + 0.5);
+    CacheExtentNodeVisitor v(extent);
+
+    root->accept(v);
+
+    while (!viewer.done())
+    {        
+        viewer.frame();
+    }
+    return 0;
 }
diff --git a/src/applications/osgearth_manip/osgearth_manip.cpp b/src/applications/osgearth_manip/osgearth_manip.cpp
index b1cc2b5..9e89536 100644
--- a/src/applications/osgearth_manip/osgearth_manip.cpp
+++ b/src/applications/osgearth_manip/osgearth_manip.cpp
@@ -40,6 +40,7 @@
 #include <osgEarthUtil/Controls>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/LogarithmicDepthBuffer>
+#include <osgEarthUtil/ViewFitter>
 #include <osgEarthAnnotation/AnnotationUtils>
 #include <osgEarthAnnotation/LabelNode>
 #include <osgEarthSymbology/Style>
@@ -520,6 +521,48 @@ namespace
         osg::ref_ptr<EarthManipulator> _manip;
         double _vfov, _ar, _zn, _zf;
     };
+    
+    struct FitViewToPoints : public osgGA::GUIEventHandler
+    {
+        std::vector<GeoPoint> _points;
+        const SpatialReference* _mapSRS;
+
+        FitViewToPoints(char key, EarthManipulator* manip, const SpatialReference* mapSRS)
+            : _key(key), _manip(manip), _mapSRS(mapSRS)
+        {
+            // Set up a list of control points
+            const SpatialReference* srs = SpatialReference::get("wgs84");
+            _points.push_back(GeoPoint(srs, -120, 30, 0));
+            _points.push_back(GeoPoint(srs, -100, 45, 0));
+        }
+
+        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
+        {
+            if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _key)
+            {
+                ViewFitter fitter(_mapSRS, aa.asView()->getCamera());
+                fitter.setBuffer( 100000.0 );
+                Viewpoint vp;
+                if (fitter.createViewpoint(_points, vp))
+                {
+                    _manip->setViewpoint(vp);
+                    aa.requestRedraw();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        void getUsage(osg::ApplicationUsage& usage) const
+        {
+            using namespace std;
+            usage.addKeyboardMouseBinding(string(1, _key), string("FitViewToPoints"));
+        }
+
+        char _key;
+        osg::ref_ptr<EarthManipulator> _manip;
+    };
+        
 
     /**
      * A simple simulator that moves an object around the Earth. We use this to
@@ -638,23 +681,23 @@ int main(int argc, char** argv)
     osgEarth::MapNode* mapNode = osgEarth::MapNode::findMapNode( earthNode );
 
     // user model?
-    osg::Node* model = 0L;
+    osg::ref_ptr<osg::Node> model;
     std::string modelFile;
     if (arguments.read("--model", modelFile))
-        model = osgDB::readNodeFile(modelFile + ".osgearth_shadergen");
+        model = osgDB::readRefNodeFile(modelFile + ".osgearth_shadergen");
 
     osg::Group* sims = new osg::Group();
     root->addChild( sims );
 
     // Simulator for tethering:
-    Simulator* sim1 = new Simulator(sims, manip, mapNode, model, "Thing 1", '8');
+    Simulator* sim1 = new Simulator(sims, manip, mapNode, model.get(), "Thing 1", '8');
     sim1->_lat0 = 55.0;
     sim1->_lon0 = 45.0;
     sim1->_lat1 = -55.0;
     sim1->_lon1 = -45.0;
     viewer.addEventHandler(sim1);
 
-    Simulator* sim2 = new Simulator(sims, manip, mapNode, model, "Thing 2", '9');
+    Simulator* sim2 = new Simulator(sims, manip, mapNode, model.get(), "Thing 2", '9');
     sim2->_name = "Thing 2";
     sim2->_lat0 = 54.0;
     sim2->_lon0 = 45.0;
@@ -697,6 +740,8 @@ int main(int argc, char** argv)
     viewer.addEventHandler(new ToggleLDB('L'));
     viewer.addEventHandler(new ToggleSSL(sims, ')'));
 
+    viewer.addEventHandler(new FitViewToPoints('j', manip, mapNode->getMapSRS()));
+
     viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
 
     while(!viewer.done())
diff --git a/src/applications/osgearth_map/osgearth_map.cpp b/src/applications/osgearth_map/osgearth_map.cpp
index 5e7a833..1b150d1 100644
--- a/src/applications/osgearth_map/osgearth_map.cpp
+++ b/src/applications/osgearth_map/osgearth_map.cpp
@@ -26,11 +26,15 @@
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/GeoTransform>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/AutoClipPlaneHandler>
 #include <osgEarthUtil/Controls>
 #include <osgEarthSymbology/Color>
 #include <osgEarthDrivers/tms/TMSOptions>
+#include <osgEarthDrivers/wms/WMSOptions>
+#include <osgEarthDrivers/gdal/GDALOptions>
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
@@ -44,22 +48,49 @@ main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
 
-    // create the map.
+    // create the empty map.
     Map* map = new Map();
 
-    // add a TMS imager layer:
+    // add a TMS imagery layer:
     TMSOptions imagery;
     imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/7/";
-    map->addImageLayer( new ImageLayer("Imagery", imagery) );
+    map->addLayer( new ImageLayer("ReadyMap Imagery", imagery) );
 
     // add a TMS elevation layer:
     TMSOptions elevation;
-    elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/9/";
-    map->addElevationLayer( new ElevationLayer("Elevation", elevation) );
+    elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/116/";
+    map->addLayer( new ElevationLayer("ReadyMap Elevation", elevation) );
+    
+    // add a local GeoTIFF inset layer:
+    GDALOptions gdal;
+    gdal.url() = "../data/boston-inset.tif";
+    map->addLayer(new ImageLayer("Boston", gdal));
+
+    // add a WMS radar layer with transparency, and disable caching since
+    // this layer updates on the server periodically.
+    WMSOptions wms;
+    wms.url() = "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi";
+    wms.format() = "png";
+    wms.layers() = "nexrad-n0r";
+    wms.srs() = "EPSG:4326";
+    wms.transparent() = true;
+    ImageLayerOptions wmsLayerOptions("WMS NEXRAD", wms);
+    wmsLayerOptions.cachePolicy() = CachePolicy::NO_CACHE;
+    map->addLayer(new ImageLayer(wmsLayerOptions));
 
     // make the map scene graph:
     MapNode* node = new MapNode( map );
 
+    // put a model on the map atop Pike's Peak, Colorado, USA
+    osg::ref_ptr<osg::Node> model = osgDB::readRefNodeFile("../data/red_flag.osg.10000.scale.osgearth_shadergen");
+    if (model.valid())
+    {
+        GeoTransform* xform = new GeoTransform();
+        xform->addChild(model.get());
+        xform->setPosition(GeoPoint(map->getSRS()->getGeographicSRS(), -105.042292, 38.840829));
+        node->addChild(xform);
+    }
+
     // initialize a viewer:
     osgViewer::Viewer viewer(arguments);
     viewer.setCameraManipulator( new EarthManipulator );
@@ -74,4 +105,4 @@ main(int argc, char** argv)
     viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
 
     return viewer.run();
-}
+}
\ No newline at end of file
diff --git a/src/applications/osgearth_minimap/osgearth_minimap.cpp b/src/applications/osgearth_minimap/osgearth_minimap.cpp
index 40c5e7d..57d1737 100644
--- a/src/applications/osgearth_minimap/osgearth_minimap.cpp
+++ b/src/applications/osgearth_minimap/osgearth_minimap.cpp
@@ -28,6 +28,7 @@
 #include <osgEarthAnnotation/FeatureNode>
 #include <osgViewer/CompositeViewer>
 #include <osgEarthDrivers/gdal/GDALOptions>
+#include <osgEarth/ImageLayer>
 
 #define LC "[viewer] "
 
@@ -48,7 +49,7 @@ MapNode* makeMiniMapNode( ) {
 
     GDALOptions basemapOpt;
     basemapOpt.url() = "../data/world.tif";
-    map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) );
+    map->addLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) );
 
     // That's it, the map is ready; now create a MapNode to render the Map:
     MapNodeOptions mapNodeOptions;
@@ -147,7 +148,6 @@ osgEarth::GeoExtent getExtent(osgViewer::View* view)
     double maxLat = osg::clampBelow(center.y() + radiusDegrees, 90.0);
 
     osgEarth::GeoExtent extent(srs, minLon, minLat, maxLon, maxLat);
-    extent.normalize();
 
     return extent;
 }
@@ -156,8 +156,6 @@ int
 main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
-    if ( arguments.read("--stencil") )
-        osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
 
     //Setup a CompositeViewer
     osgViewer::CompositeViewer viewer(arguments);
diff --git a/src/applications/osgearth_mrt/osgearth_mrt.cpp b/src/applications/osgearth_mrt/osgearth_mrt.cpp
index 2b7bf92..4ecbda9 100644
--- a/src/applications/osgearth_mrt/osgearth_mrt.cpp
+++ b/src/applications/osgearth_mrt/osgearth_mrt.cpp
@@ -126,61 +126,50 @@ createFramebufferPass(App& app)
     osg::StateSet* stateset = quad->getOrCreateStateSet();
 
     static const char* vertSource =
-        "varying vec4 texcoord;\n"
+        "out vec4 texcoord;\n"
         "void effect_vert(inout vec4 vertexView)\n"
         "{\n"
         "    texcoord = gl_MultiTexCoord0; \n"
         "}\n";
 
+    // fragment shader that performs edge detection and tints edges red.
     static const char* fragSource =
         "#version " GLSL_VERSION_STR "\n"
         "#extension GL_ARB_texture_rectangle : enable\n"
         "uniform sampler2DRect gcolor;\n"
         "uniform sampler2DRect gnormal;\n"
         "uniform sampler2DRect gdepth;\n"
-        "varying vec4 texcoord;\n"
+        "uniform float osg_FrameTime;\n"
+        "in vec4 texcoord;\n"
 
         "void effect_frag(inout vec4 color)\n"
         "{\n"
-        "    color = texture2DRect(gcolor, texcoord.st); \n"
-        "    float depth = texture2DRect(gdepth, texcoord.st).r; \n"
-        "    vec3 normal = texture2DRect(gnormal,texcoord.st).xyz *2.0-1.0; \n"
+        "    color = texture(gcolor, texcoord.st); \n"
+        "    float depth = texture(gdepth, texcoord.st).r; \n"
+        "    vec3 normal = texture(gnormal,texcoord.st).xyz *2.0-1.0; \n"
 
         // sample radius in pixels:
-        "    float e = 5.0; \n"
+        "    float e = 25.0 * sin(osg_FrameTime); \n"
 
         // sample the normals around our pixel and find the approximate
         // deviation from our center normal:
         "    vec3 avgNormal =\n"
-        "       texture2DRect(gnormal, texcoord.st+vec2( e, e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2(-e, e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2(-e,-e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2( e,-e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2( 0, e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2( e, 0)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2( 0,-e)).xyz + \n"
-        "       texture2DRect(gnormal, texcoord.st+vec2(-e, 0)).xyz;  \n"
+        "       texture(gnormal, texcoord.st+vec2( e, e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2(-e, e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2(-e,-e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2( e,-e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2( 0, e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2( e, 0)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2( 0,-e)).xyz + \n"
+        "       texture(gnormal, texcoord.st+vec2(-e, 0)).xyz;  \n"
         "    avgNormal = normalize((avgNormal/8.0)*2.0-1.0); \n"
+
+        // average deviation from normal:
         "    float deviation = clamp(dot(normal, avgNormal),0.0,1.0); \n"
 
-        // set a blur factor based on the normal deviation, so that we
-        // blur more around edges.
+        // use that to tint the pixel red:
         "    e = 2.5 * (1.0-deviation); \n"
-
-        "    vec4 blurColor = \n"
-        "       color + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2( e, e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2(-e, e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2(-e,-e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2( e,-e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2( 0, e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2( e, 0)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2( 0,-e)) + \n"
-        "       texture2DRect(gcolor, texcoord.st+vec2(-e, 0));  \n"
-        "    blurColor /= 9.0; \n"
-
-        // blur the color and darken the edges at the same time
-        "    color.rgb = blurColor.rgb * deviation; \n"
+        "    color.rgb = color.rgb + vec3(e,0,0);\n"
         "}\n";
 
     VirtualProgram* vp = VirtualProgram::getOrCreate(stateset);
diff --git a/src/applications/osgearth_featureeditor/CMakeLists.txt b/src/applications/osgearth_noisegen/CMakeLists.txt
similarity index 67%
rename from src/applications/osgearth_featureeditor/CMakeLists.txt
rename to src/applications/osgearth_noisegen/CMakeLists.txt
index b35afd6..324469d 100644
--- a/src/applications/osgearth_featureeditor/CMakeLists.txt
+++ b/src/applications/osgearth_noisegen/CMakeLists.txt
@@ -1,8 +1,7 @@
 INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
-
 SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
 
-SET(TARGET_SRC osgearth_featureeditor.cpp )
+SET(TARGET_SRC osgearth_noisegen.cpp )
 
 #### end var setup  ###
-SETUP_APPLICATION(osgearth_featureeditor)
\ No newline at end of file
+SETUP_APPLICATION(osgearth_noisegen)
\ No newline at end of file
diff --git a/src/applications/osgearth_map/osgearth_map.cpp b/src/applications/osgearth_noisegen/osgearth_noisegen.cpp
similarity index 57%
copy from src/applications/osgearth_map/osgearth_map.cpp
copy to src/applications/osgearth_noisegen/osgearth_noisegen.cpp
index 5e7a833..c18826c 100644
--- a/src/applications/osgearth_map/osgearth_map.cpp
+++ b/src/applications/osgearth_noisegen/osgearth_noisegen.cpp
@@ -26,16 +26,31 @@
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/MapNode>
-#include <osgEarthUtil/EarthManipulator>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/SimplexNoise>
 #include <osgEarthUtil/AutoClipPlaneHandler>
 #include <osgEarthUtil/Controls>
 #include <osgEarthSymbology/Color>
 #include <osgEarthDrivers/tms/TMSOptions>
+#include <osgEarthDrivers/wms/WMSOptions>
+#include <osgEarthDrivers/gdal/GDALOptions>
+#include <osgDB/WriteFile>
+#include <osg/Image>
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
 using namespace osgEarth::Util;
 
+int usage()
+{
+    OE_WARN << "\n"
+        "osgearth_noisegen --size n             ; image dimension\n"
+        "                  --out string         ; output filename\n"
+        "                  [--frequency n]      ; default = 16\n"
+        "                  [--octaves n]        ; default = 12\n"
+        ;
+    return -1;
+}
 /**
  * How to create a simple osgEarth map and display it.
  */
@@ -44,34 +59,25 @@ main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
 
-    // create the map.
-    Map* map = new Map();
-
-    // add a TMS imager layer:
-    TMSOptions imagery;
-    imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/7/";
-    map->addImageLayer( new ImageLayer("Imagery", imagery) );
-
-    // add a TMS elevation layer:
-    TMSOptions elevation;
-    elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/9/";
-    map->addElevationLayer( new ElevationLayer("Elevation", elevation) );
+    int dim;
+    if (!arguments.read("--size", dim))
+        return usage();
 
-    // make the map scene graph:
-    MapNode* node = new MapNode( map );
+    std::string out;
+    if (!arguments.read("--out", out))
+        return usage();
 
-    // initialize a viewer:
-    osgViewer::Viewer viewer(arguments);
-    viewer.setCameraManipulator( new EarthManipulator );
-    viewer.setSceneData( node );
+    double freq = 16.0;
+    arguments.read("--frequency", freq);
 
-    // add some stock OSG handlers:
-    viewer.addEventHandler(new osgViewer::StatsHandler());
-    viewer.addEventHandler(new osgViewer::WindowSizeHandler());
-    viewer.addEventHandler(new osgViewer::ThreadingHandler());
-    viewer.addEventHandler(new osgViewer::LODScaleHandler());
-    viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()));
-    viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
+    int octaves = 12;
+    arguments.read("--octaves", octaves);
 
-    return viewer.run();
+    SimplexNoise noise;
+    noise.setFrequency(freq);
+    noise.setOctaves(octaves);
+    noise.setNormalize(true);
+    osg::Image* image = noise.createSeamlessImage(dim);
+    osgDB::writeImageFile(*image, out);
+    return 0;
 }
diff --git a/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp b/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
index d1622e8..5d25ba1 100644
--- a/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
+++ b/src/applications/osgearth_occlusionculling/osgearth_occlusionculling.cpp
@@ -107,7 +107,7 @@ main(int argc, char** argv)
 
     //Create a bunch of placemarks around Mt Rainer so we can actually get some elevation
     {
-        osg::Image* pin = osgDB::readImageFile( "../data/placemark32.png" );
+        osg::ref_ptr<osg::Image> pin = osgDB::readRefImageFile( "../data/placemark32.png" );
 
         double centerLat =  46.840866;
         double centerLon = -121.769846;
@@ -122,7 +122,7 @@ main(int argc, char** argv)
         {
             double lat = minLat + height * (rand() * 1.0)/(RAND_MAX-1);
             double lon = minLon + width * (rand() * 1.0)/(RAND_MAX-1);        
-            PlaceNode* place = new PlaceNode(mapNode, GeoPoint(geoSRS, lon, lat), pin, "Placemark", placeStyle);
+            PlaceNode* place = new PlaceNode(mapNode, GeoPoint(geoSRS, lon, lat), pin.get(), "Placemark", placeStyle);
             //Enable occlusion culling.  This will hide placemarks that are hidden behind terrain.
             //This makes use of the OcclusionCullingCallback in CullingUtils.
             place->setOcclusionCulling( true );
diff --git a/src/applications/osgearth_package/osgearth_package.cpp b/src/applications/osgearth_package/osgearth_package.cpp
index eea47d4..cc2b786 100644
--- a/src/applications/osgearth_package/osgearth_package.cpp
+++ b/src/applications/osgearth_package/osgearth_package.cpp
@@ -29,7 +29,13 @@
 #include <osgEarth/StringUtils>
 #include <osgEarth/HTTPClient>
 #include <osgEarth/TileVisitor>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
+
+#include <osgEarthFeatures/FeatureCursor>
+
 #include <osgEarthUtil/TMSPackager>
+
 #include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 #include <osgEarthDrivers/tms/TMSOptions>
 
@@ -306,7 +312,7 @@ makeTMS( osg::ArgumentParser& args )
 
     if (verbose)
     {
-        visitor->setProgressCallback( progress );
+        visitor->setProgressCallback( progress.get() );
     }
 
     visitor->setMinLevel( minLevel );
@@ -324,10 +330,10 @@ makeTMS( osg::ArgumentParser& args )
     // Setup a TMSPackager with all the options.
     TMSPackager packager;
     packager.setExtension(extension);
-    packager.setVisitor(visitor);
+    packager.setVisitor(visitor.get());
     packager.setDestination(rootFolder);    
     packager.setElevationPixelDepth(elevationPixelDepth);
-    packager.setWriteOptions(options);    
+    packager.setWriteOptions(options.get());    
     packager.setOverwrite(overwrite);
     packager.setKeepEmpties(keepEmpties);
     packager.setApplyAlphaMask(applyAlphaMask);
@@ -338,7 +344,7 @@ makeTMS( osg::ArgumentParser& args )
     if( !outEarth.empty() )
     {
         // copy the options from the source map first
-        outMap = new Map( map->getInitialMapOptions() );
+        outMap = new Map(); //new Map( map->getInitialMapOptions() );
     }
 
     std::string outEarthFile = osgDB::concatPaths( rootFolder, osgDB::getSimpleFileName( outEarth ) );
@@ -347,7 +353,7 @@ makeTMS( osg::ArgumentParser& args )
     // Package an individual image layer
     if (imageLayerIndex >= 0)
     {        
-        ImageLayer* layer = map->getImageLayerAt(imageLayerIndex);
+        ImageLayer* layer = map->getLayerAt<ImageLayer>(imageLayerIndex);
         if (layer)
         {
             packager.run(layer, map);
@@ -365,7 +371,7 @@ makeTMS( osg::ArgumentParser& args )
     // Package an individual elevation layer
     else if (elevationLayerIndex >= 0)
     {        
-        ElevationLayer* layer = map->getElevationLayerAt(elevationLayerIndex);
+        ElevationLayer* layer = map->getLayerAt<ElevationLayer>(elevationLayerIndex);
         if (layer)
         {
             packager.run(layer, map);
@@ -381,11 +387,14 @@ makeTMS( osg::ArgumentParser& args )
         }
     }
     else
-    {        
+    {
+        ImageLayerVector imageLayers;
+        map->getLayers(imageLayers);
+
         // Package all the ImageLayer's
-        for (unsigned int i = 0; i < map->getNumImageLayers(); i++)
+        for (unsigned int i = 0; i < imageLayers.size(); i++)
         {            
-            ImageLayer* layer = map->getImageLayerAt(i);        
+            ImageLayer* layer = imageLayers[i].get();
             OE_NOTICE << "Packaging " << layer->getName() << std::endl;
             osg::Timer_t start = osg::Timer::instance()->tick();
             packager.run(layer, map);
@@ -412,17 +421,18 @@ makeTMS( osg::ArgumentParser& args )
                     outEarthFile );
 
                 ImageLayerOptions layerOptions( packager.getLayerName(), tms );
-                layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) );
-                layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
 
-                outMap->addImageLayer( new ImageLayer( layerOptions ) );
+                outMap->addLayer( new ImageLayer( layerOptions ) );
             }
         }    
 
         // Package all the ElevationLayer's
-        for (unsigned int i = 0; i < map->getNumElevationLayers(); i++)
+        ElevationLayerVector elevationLayers;
+        map->getLayers(elevationLayers);
+
+        for (unsigned int i = 0; i < elevationLayers.size(); i++)
         {            
-            ElevationLayer* layer = map->getElevationLayerAt(i);        
+            ElevationLayer* layer = elevationLayers[i].get();
             OE_NOTICE << "Packaging " << layer->getName() << std::endl;
             osg::Timer_t start = osg::Timer::instance()->tick();
             packager.run(layer, map);
@@ -448,10 +458,8 @@ makeTMS( osg::ArgumentParser& args )
                     outEarthFile );
 
                 ElevationLayerOptions layerOptions( packager.getLayerName(), tms );
-                layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) );
-                layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
 
-                outMap->addElevationLayer( new ElevationLayer( layerOptions ) );
+                outMap->addLayer( new ElevationLayer( layerOptions ) );
             }
         }
 
diff --git a/src/applications/osgearth_package_qt/CMakeLists.txt b/src/applications/osgearth_package_qt/CMakeLists.txt
index 5b9ccb1..32c92f7 100644
--- a/src/applications/osgearth_package_qt/CMakeLists.txt
+++ b/src/applications/osgearth_package_qt/CMakeLists.txt
@@ -24,12 +24,14 @@ IF(Qt5Widgets_FOUND)
         QT5_WRAP_CPP( MOC_SRC ${MOC_HDR} OPTIONS "-f${MOC_HDR_ABS}" )
         LIST( APPEND MOC_SRCS ${MOC_SRC} )
     ENDFOREACH()
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt5)
 ELSE()
     INCLUDE( ${QT_USE_FILE} )
     QT4_ADD_RESOURCES( LIB_RC_SRCS ${LIB_QT_RCS} )
     QT4_WRAP_UI( UI_HDRS ${UI_FILES} )
     QT4_WRAP_CPP( UI_SRCS ${UI_HDRS} )
     QT4_WRAP_CPP( MOC_SRCS ${MOC_HDRS} OPTIONS "-f" )
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt)
 ENDIF()
 
 SET(TARGET_H
@@ -56,11 +58,15 @@ SET(TARGET_SRC
 )
 
 SET(TARGET_ADDED_LIBRARIES
-    osgEarthQt
+    ${OSGEARTH_QT_LIBRARY}
     ${QT_QTCORE_LIBRARY}
     ${QT_QTGUI_LIBRARY}
     ${QT_QTOPENGL_LIBRARY}
 )
 
 #### end var setup  ###
+IF(Qt5Widgets_FOUND)
+SETUP_APPLICATION(osgearth_package_qt5)
+ELSE(Qt5Widgets_FOUND)
 SETUP_APPLICATION(osgearth_package_qt)
+ENDIF(Qt5Widgets_FOUND)
diff --git a/src/applications/osgearth_package_qt/ExportDialog.cpp b/src/applications/osgearth_package_qt/ExportDialog.cpp
index 57a5ffe..3f98f84 100644
--- a/src/applications/osgearth_package_qt/ExportDialog.cpp
+++ b/src/applications/osgearth_package_qt/ExportDialog.cpp
@@ -137,6 +137,9 @@ void ExportDialog::updateEstimate()
         est.addExtent(GeoExtent(_mapNode->getMapSRS(), _bounds));
     }    
 
+    TerrainLayerVector terrainLayers;
+    _mapNode->getMap()->getLayers(terrainLayers);
+
     int maxLevel = 10;
     if (maxLevelEnabled())
     {
@@ -146,9 +149,9 @@ void ExportDialog::updateEstimate()
     {
         // Determine the max level from the layers
         maxLevel = 0;
-        for (unsigned int i = 0; i < _mapNode->getMap()->getNumImageLayers(); i++)
+        for (unsigned int i = 0; i < terrainLayers.size(); i++)
         {
-            osgEarth::ImageLayer* layer = _mapNode->getMap()->getImageLayerAt(i);
+            osgEarth::TerrainLayer* layer = terrainLayers[i].get();
             if (layer)
             {
                 osgEarth::TileSource* ts = layer->getTileSource();
@@ -165,24 +168,24 @@ void ExportDialog::updateEstimate()
             }
         }
 
-        for (unsigned int i = 0; i < _mapNode->getMap()->getNumElevationLayers(); i++)
-        {
-            osgEarth::ElevationLayer* layer = _mapNode->getMap()->getElevationLayerAt(i);
-            if (layer)
-            {
-                osgEarth::TileSource* ts = layer->getTileSource();
-                if (ts)
-                {
-                    for (DataExtentList::iterator itr = ts->getDataExtents().begin(); itr != ts->getDataExtents().end(); itr++)
-                    {
-                        if (itr->maxLevel().isSet() && itr->maxLevel().value() > maxLevel)
-                        {
-                            maxLevel = itr->maxLevel().value();
-                        }
-                    }
-                }
-            }
-        }
+        //for (unsigned int i = 0; i < _mapNode->getMap()->getNumElevationLayers(); i++)
+        //{
+        //    osgEarth::ElevationLayer* layer = _mapNode->getMap()->getElevationLayerAt(i);
+        //    if (layer)
+        //    {
+        //        osgEarth::TileSource* ts = layer->getTileSource();
+        //        if (ts)
+        //        {
+        //            for (DataExtentList::iterator itr = ts->getDataExtents().begin(); itr != ts->getDataExtents().end(); itr++)
+        //            {
+        //                if (itr->maxLevel().isSet() && itr->maxLevel().value() > maxLevel)
+        //                {
+        //                    maxLevel = itr->maxLevel().value();
+        //                }
+        //            }
+        //        }
+        //    }
+        //}
     }
 
     est.setMaxLevel(maxLevel);    
@@ -197,7 +200,7 @@ void ExportDialog::updateEstimate()
     }
 
     // Adjust everything by the # of layers
-    unsigned int numLayers = _mapNode->getMap()->getNumImageLayers() + _mapNode->getMap()->getNumElevationLayers();
+    unsigned int numLayers = terrainLayers.size(); //_mapNode->getMap()->getNumImageLayers() + _mapNode->getMap()->getNumElevationLayers();
     totalSeconds *= (double)numLayers;
     unsigned int numTiles = est.getNumTiles() * numLayers;
     double sizeMB = est.getSizeInMB() * (double)numLayers;
diff --git a/src/applications/osgearth_package_qt/PackageQtMainWindow b/src/applications/osgearth_package_qt/PackageQtMainWindow
index 15d5629..9bba197 100644
--- a/src/applications/osgearth_package_qt/PackageQtMainWindow
+++ b/src/applications/osgearth_package_qt/PackageQtMainWindow
@@ -141,7 +141,7 @@ private slots:
           std::string fileName = osgDB::getSimpleFileName(filePath.toStdString());
           osg::ref_ptr<osgEarth::ImageLayer> newLayer = new osgEarth::ImageLayer(osgEarth::ImageLayerOptions(fileName, layerOpt));
 
-          _manager->map()->addImageLayer(newLayer.get());
+          _manager->map()->addLayer(newLayer.get());
         }
       }
 
@@ -171,14 +171,16 @@ private slots:
             pathSet = true;
           }
 
-          osgEarth::Drivers::GDALOptions layerOpt;
-          layerOpt.url() = osgEarth::URI(filePath.toStdString());
+          osgEarth::Drivers::GDALOptions gdal;
+          gdal.url() = osgEarth::URI(filePath.toStdString());
+          std::string fileName = osgDB::getSimpleFileName(filePath.toStdString());
+
+          osgEarth::ElevationLayerOptions layerOpt(fileName, gdal);
           layerOpt.tileSize() = 15;
           
-          std::string fileName = osgDB::getSimpleFileName(filePath.toStdString());
-          osg::ref_ptr<osgEarth::ElevationLayer> newLayer = new osgEarth::ElevationLayer(osgEarth::ElevationLayerOptions(fileName, layerOpt));
+          osg::ref_ptr<osgEarth::ElevationLayer> newLayer = new osgEarth::ElevationLayer(layerOpt);
 
-          _manager->map()->addElevationLayer(newLayer.get());
+          _manager->map()->addLayer(newLayer.get());
         }
       }
 
diff --git a/src/applications/osgearth_package_qt/TMSExporter.cpp b/src/applications/osgearth_package_qt/TMSExporter.cpp
index 94bd8aa..5a7d024 100644
--- a/src/applications/osgearth_package_qt/TMSExporter.cpp
+++ b/src/applications/osgearth_package_qt/TMSExporter.cpp
@@ -30,6 +30,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/Map>
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
 #include <osgEarth/Registry>
 #include <osgEarth/StringUtils>
 #include <osgEarth/FileUtils>
@@ -156,19 +157,25 @@ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& earthFilePath, c
     packager.getTileVisitor()->setMaxLevel(_maxLevel);
 
     // Compute the total number of layers we are going to operate on.
-    unsigned int totalLayers = map->getNumImageLayers() + map->getNumElevationLayers();  
+    ImageLayerVector imageLayers;
+    map->getLayers(imageLayers);
+
+    ElevationLayerVector elevationLayers;
+    map->getLayers(elevationLayers);
+
+    unsigned int totalLayers = imageLayers.size() + elevationLayers.size();
 
     unsigned int layerNum = 1;
 
     // Package each image layer
-    for (unsigned int i = 0; i < map->getNumImageLayers(); i++)
+    for (unsigned int i = 0; i < imageLayers.size(); i++)
     {            
         // Don't continue if the export has been canceled
         if (_progress->isCanceled())
         {
             break;
         }
-        osg::ref_ptr< ImageLayer > layer = map->getImageLayerAt(i);      
+        osg::ref_ptr< ImageLayer > layer = imageLayers[i].get();
         std::stringstream buf;
         buf << "Packaging " << layer->getName() << " (" << layerNum << " of " << totalLayers << ")";
         OE_NOTICE << buf.str() << std::endl;
@@ -186,16 +193,16 @@ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& earthFilePath, c
                 outEarthFile );
 
             ImageLayerOptions layerOptions( packager.getLayerName(), tms );
-            layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) );
+            layerOptions.mergeConfig( layer->options().getConfig() );
             layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
 
-            outMap->addImageLayer( new ImageLayer( layerOptions ) );
+            outMap->addLayer( new ImageLayer( layerOptions ) );
         }
         layerNum++;
     }
 
     // Package each elevation layer
-    for (unsigned int i = 0; i < map->getNumElevationLayers(); i++)
+    for (unsigned int i = 0; i < elevationLayers.size(); i++)
     {
         // Don't continue if the export has been canceled
         if (_progress->isCanceled())
@@ -203,7 +210,7 @@ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& earthFilePath, c
             break;
         }
 
-        osg::ref_ptr< ElevationLayer > layer = map->getElevationLayerAt(i);      
+        osg::ref_ptr< ElevationLayer > layer = elevationLayers[i].get();
         std::stringstream buf;
         buf << "Packaging " << layer->getName() << " (" << layerNum << " of " << totalLayers << ")";
         OE_NOTICE << buf.str() << std::endl;
@@ -222,10 +229,10 @@ int TMSExporter::exportTMS(MapNode* mapNode, const std::string& earthFilePath, c
                 outEarthFile );
 
             ElevationLayerOptions layerOptions( packager.getLayerName(), tms );
-            layerOptions.mergeConfig( layer->getInitialOptions().getConfig( true ) );
+            layerOptions.mergeConfig( layer->options().getConfig() );
             layerOptions.cachePolicy() = CachePolicy::NO_CACHE;
 
-            outMap->addElevationLayer( new ElevationLayer( layerOptions ) );
+            outMap->addLayer( new ElevationLayer( layerOptions ) );
         }
 
         layerNum++;
diff --git a/src/applications/osgearth_pagingtest/osgearth_pagingtest.cpp b/src/applications/osgearth_pagingtest/osgearth_pagingtest.cpp
index 1e22070..327dd0c 100644
--- a/src/applications/osgearth_pagingtest/osgearth_pagingtest.cpp
+++ b/src/applications/osgearth_pagingtest/osgearth_pagingtest.cpp
@@ -123,7 +123,7 @@ public:
                   style = itr->second;
               }
 
-              FeatureNode* featureNode = new FeatureNode(_mapNode, features, style, GeometryCompilerOptions(), _styleSheet.get() );
+              FeatureNode* featureNode = new FeatureNode(_mapNode.get(), features, style, GeometryCompilerOptions(), _styleSheet.get() );
               return featureNode;
           }
           else
@@ -142,7 +142,7 @@ public:
 
       StyleSheet* getStyleSheet() const
       {
-          return _styleSheet;
+          return _styleSheet.get();
       }
 
       void setStyleSheet(StyleSheet* styleSheet)
@@ -273,7 +273,7 @@ int main(int argc, char** argv)
                 return -1;
             }
 
-            FeaturePager* featurePager = new FeaturePager(features, getStyle(randomColor(), 0.0), mapNode);
+            FeaturePager* featurePager = new FeaturePager(features.get(), getStyle(randomColor(), 0.0), mapNode);
 
             // Style 13 is where the full resolution data comes, in so use a fancy textured and extruded style
             // a style for the building data:
diff --git a/src/applications/osgearth_pick/osgearth_pick.cpp b/src/applications/osgearth_pick/osgearth_pick.cpp
index 6f75fc6..a9dcbec 100644
--- a/src/applications/osgearth_pick/osgearth_pick.cpp
+++ b/src/applications/osgearth_pick/osgearth_pick.cpp
@@ -46,102 +46,69 @@ using namespace osgEarth::Annotation;
 
 namespace ui = osgEarth::Util::Controls;
 
-static ui::LabelControl* s_fidLabel;
-static ui::LabelControl* s_nameLabel;
-static osg::Uniform*     s_highlightUniform;
-
 //-----------------------------------------------------------------------
 
-// Tests the (old) intersection-based picker.
-struct TestIsectPicker : public osgGA::GUIEventHandler
+//! Application-wide data.
+struct App
 {
-    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
-    {
-        if ( ea.getEventType() == ea.RELEASE )
-        {
-            IntersectionPicker picker(dynamic_cast<osgViewer::View*>(aa.asView()));
-            IntersectionPicker::Hits hits;
-            if(picker.pick(ea.getX(), ea.getY(), hits)) {
-                std::set<ObjectID> oids;
-                if (picker.getObjectIDs(hits, oids)) {
-                    ObjectIndex* index = Registry::objectIndex();
-                    ObjectID oid = *oids.begin();
-                    osg::ref_ptr<FeatureIndex> fi = index->get<FeatureIndex>(oid);
-                    if ( fi.valid() ) {
-                        OE_NOTICE << "IsectPicker: found OID " << oid << "\n";
-                        Feature* f = fi->getFeature(oid);
-                        if ( f ) {
-                            OE_NOTICE << "...feature ID = " << f->getFID() << "\n";
-                        }
-                    }      
-                    osg::ref_ptr<Feature> f = index->get<Feature>(oid);
-                    if ( f.valid() ) {
-                        OE_NOTICE << "IsectPicker: found OID " << oid << "\n";
-                        OE_NOTICE << "...feature ID = " << f->getFID() << "\n";
-                    }
-                    osg::ref_ptr<AnnotationNode> a = index->get<AnnotationNode>(oid);
-                    if ( a ) {
-                        OE_NOTICE << "IsectPicker: found annotation " << a->getName() << "\n";
-                    }
-                }
-                else {
-                    OE_NOTICE << "IsectPicker: picked, but no OIDs\n";
-                }
-            }
-            else {
-                OE_NOTICE << "IsectPicker: no intersect\n";
-            }
-        }
-        return false;
-    }
-};
+    App(osg::ArgumentParser& args) : viewer(args), mainView(NULL), rttView(NULL), mapNode(NULL), picker(NULL) { }
 
+    osgViewer::CompositeViewer viewer;
+    osgViewer::View* mainView;
+    osgViewer::View* rttView;
+    osgEarth::MapNode* mapNode;
+    osgEarth::Util::RTTPicker* picker;
 
-//-----------------------------------------------------------------------
+    ui::LabelControl* fidLabel;
+    ui::LabelControl* nameLabel;
+    osg::Uniform*     highlightUniform;
+};
 
-/**
- * Callback that you install on the RTTPicker.
- */
+
+//! Callback that you install on the RTTPicker.
 struct MyPickCallback : public RTTPicker::Callback
 {
+    App& _app;
+    MyPickCallback(App& app) : _app(app) { }
+
     void onHit(ObjectID id)
     {
         // First see whether it's a feature:
-        FeatureIndex* index = Registry::objectIndex()->get<FeatureIndex>( id );
+        FeatureIndex* index = Registry::objectIndex()->get<FeatureIndex>(id).get();
         Feature* feature = index ? index->getFeature( id ) : 0L;
 
         if ( feature )
         {
-            s_fidLabel->setText( Stringify() << "Feature ID = " << feature->getFID() << " (oid = " << id << ")" );
-            s_nameLabel->setText( Stringify() << "Name = " << feature->getString("name") );
+            _app.fidLabel->setText( Stringify() << "Feature ID = " << feature->getFID() << " (oid = " << id << ")" );
+            _app.nameLabel->setText( Stringify() << "Name = " << feature->getString("name") );
         }
 
         else
         {
             // Check whether it's an annotation:
-            AnnotationNode* anno = Registry::objectIndex()->get<AnnotationNode>( id );
+            AnnotationNode* anno = Registry::objectIndex()->get<AnnotationNode>(id).get();
             if ( anno )
             {
-                s_fidLabel->setText( Stringify() << "ObjectID = " << id );
-                s_nameLabel->setName( Stringify() << "Name = " << anno->getName() );
+                _app.fidLabel->setText( Stringify() << "ObjectID = " << id );
+                _app.nameLabel->setName( Stringify() << "Name = " << anno->getName() );
             }
 
             // None of the above.. clear.
             else
             {
-                s_fidLabel->setText( Stringify() << "unknown oid = " << id );
-                s_nameLabel->setText( " " );
+                _app.fidLabel->setText( Stringify() << "unknown oid = " << id );
+                _app.nameLabel->setText( " " );
             }
         }
 
-        s_highlightUniform->set( id );
+        _app.highlightUniform->set( id );
     }
 
     void onMiss()
     {
-        s_fidLabel->setText( "No pick." );
-        s_nameLabel->setText( " " );
-        s_highlightUniform->set( 0u );
+        _app.fidLabel->setText( "No pick." );
+        _app.nameLabel->setText( " " );
+        _app.highlightUniform->set( 0u );
     }
 
     // pick whenever the mouse moves.
@@ -174,8 +141,11 @@ const char* highlightFrag =
     "        color.rgb = mix(color.rgb, clamp(vec3(0.5,2.0,2.0)*(1.0-color.rgb), 0.0, 1.0), 0.5); \n"
     "} \n";
 
-void installHighlighter(osg::StateSet* stateSet, int attrLocation)
+void installHighlighter(App& app)
 {
+    osg::StateSet* stateSet = app.mapNode->getOrCreateStateSet();
+    int attrLocation = Registry::objectIndex()->getObjectIDAttribLocation();
+
     // This shader program will highlight the selected object.
     VirtualProgram* vp = VirtualProgram::getOrCreate(stateSet);
     vp->setFunction( "checkForHighlight",  highlightVert, ShaderComp::LOCATION_VERTEX_CLIP );
@@ -185,8 +155,8 @@ void installHighlighter(osg::StateSet* stateSet, int attrLocation)
     Registry::objectIndex()->loadShaders( vp );
 
     // A uniform that will tell the shader which object to highlight:
-    s_highlightUniform = new osg::Uniform("objectid_to_highlight", 0u);
-    stateSet->addUniform( s_highlightUniform );
+    app.highlightUniform = new osg::Uniform("objectid_to_highlight", 0u);
+    stateSet->addUniform(app.highlightUniform );
 }
 
 //------------------------------------------------------------------------
@@ -203,18 +173,18 @@ setupRTTView(osgViewer::View* view, osg::Texture* rttTex)
     view->getCamera()->setViewMatrixAsLookAt(osg::Vec3d(0,-1,0), osg::Vec3d(0,0,0), osg::Vec3d(0,0,1));
     view->getCamera()->setProjectionResizePolicy(osg::Camera::FIXED);
 
-    osg::Vec3Array* v = new osg::Vec3Array(4);
-    (*v)[0].set(-.5,0,-.5); (*v)[1].set(.5,0,-.5); (*v)[2].set(.5,0,.5); (*v)[3].set(-.5,0,.5);
+    osg::Vec3Array* v = new osg::Vec3Array(6);
+    (*v)[0].set(-.5,0,-.5); (*v)[1].set(.5,0,-.5); (*v)[2].set(.5,0,.5); (*v)[3].set((*v)[2]); (*v)[4].set(-.5,0,.5);(*v)[5].set((*v)[0]);
 
-    osg::Vec2Array* t = new osg::Vec2Array(4);
-    (*t)[0].set(0,0); (*t)[1].set(1,0); (*t)[2].set(1,1); (*t)[3].set(0,1);
+    osg::Vec2Array* t = new osg::Vec2Array(6);
+    (*t)[0].set(0,0); (*t)[1].set(1,0); (*t)[2].set(1,1); (*t)[3].set((*t)[2]); (*t)[4].set(0,1); (*t)[5].set((*t)[0]);
 
     osg::Geometry* g = new osg::Geometry();
     g->setUseVertexBufferObjects(true);
     g->setUseDisplayList(false);
     g->setVertexArray( v );
     g->setTexCoordArray( 0, t );
-    g->addPrimitiveSet( new osg::DrawArrays(GL_QUADS, 0, 4) );
+    g->addPrimitiveSet( new osg::DrawArrays(GL_TRIANGLES, 0, 6) );
 
     osg::Geode* geode = new osg::Geode();
     geode->addDrawable( g );
@@ -230,13 +200,71 @@ setupRTTView(osgViewer::View* view, osg::Texture* rttTex)
     stateSet->setMode(GL_CULL_FACE, 0);
     stateSet->setAttributeAndModes(new osg::BlendFunc(GL_ONE, GL_ZERO), 1);
     
-    const char* fs = "void swap(inout vec4 c) { c.rgba = c==vec4(0)? vec4(1) : vec4(vec3((c.r+c.g+c.b+c.a)/4.0),1); }\n";
+    const char* fs =
+    "#version " GLSL_VERSION_STR "\n"
+    "void swap(inout vec4 c) { c.rgba = c==vec4(0)? vec4(1) : vec4(vec3((c.r+c.g+c.b+c.a)/4.0),1); }\n";
     osgEarth::Registry::shaderGenerator().run(geode);
     VirtualProgram::getOrCreate(geode->getOrCreateStateSet())->setFunction("swap", fs, ShaderComp::LOCATION_FRAGMENT_COLORING);
 
     view->setSceneData( geode );
 }
 
+void startPicker(App& app)
+{
+    // Note! Must stop and restart threading when removing the picker
+    // because it changes the OSG View/Slave configuration.
+    app.viewer.stopThreading();
+
+    app.picker = new RTTPicker();
+    app.mainView->addEventHandler(app.picker);
+
+    // add the graph that will be picked.
+    app.picker->addChild(app.mapNode);
+
+    // install a callback that controls the picker and listens for hits.
+    app.picker->setDefaultCallback(new MyPickCallback(app));
+
+    // Make a view that lets us see what the picker sees.
+    if (app.rttView == NULL)
+    {
+        app.rttView = new osgViewer::View();
+        app.rttView->getCamera()->setGraphicsContext(app.mainView->getCamera()->getGraphicsContext());
+        app.rttView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
+        app.viewer.addView(app.rttView);    
+    }
+    setupRTTView(app.rttView, app.picker->getOrCreateTexture(app.mainView));
+    app.rttView->getCamera()->setNodeMask(~0);
+
+    app.viewer.startThreading();
+}
+
+void stopPicker(App& app)
+{
+    // Note! Must stop and restart threading when removing the picker
+    // because it changes the OSG View/Slave configuration.
+    app.viewer.stopThreading();
+
+    //app.viewer.removeView(app.rttView);
+    app.rttView->getCamera()->setNodeMask(0);
+    app.mainView->removeEventHandler(app.picker);
+    app.picker = 0L;
+
+    app.viewer.startThreading();
+}
+
+struct TogglePicker : public ui::ControlEventHandler
+{
+    App& _app;
+    TogglePicker(App& app) : _app(app) { }
+    void onClick(Control* button)
+    {
+        if (_app.picker == 0L)
+            startPicker(_app);
+        else
+            stopPicker(_app);
+    }
+};
+
 //-----------------------------------------------------------------------
 
 int
@@ -255,20 +283,16 @@ main(int argc, char** argv)
     if ( arguments.read("--help") )
         return usage(argv[0]);
 
-    // create a viewer that will hold 2 viewports.
-    osgViewer::CompositeViewer viewer(arguments);
+    App app(arguments);
 
-    osgViewer::View* mainView = new osgViewer::View();
-    mainView->setUpViewInWindow(30, 30, 1024, 1024, 0);
-    mainView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
-
-    viewer.addView(mainView);
-
-    // Tell the database pager to not modify the unref settings
-    mainView->getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
+    app.mainView = new osgViewer::View();
+    app.mainView->setUpViewInWindow(30, 30, 1024, 1024, 0);
+    app.mainView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
+    
+    app.viewer.addView(app.mainView);
 
-    // install our default manipulator (do this before calling load)
-    mainView->setCameraManipulator( new EarthManipulator() );    
+    app.mainView->getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
+    app.mainView->setCameraManipulator( new EarthManipulator() );    
 
     // Made some UI components:
     ui::VBox* uiContainer = new ui::VBox();
@@ -277,42 +301,28 @@ main(int argc, char** argv)
     uiContainer->setBackColor(0,0,0,0.8);
 
     uiContainer->addControl( new ui::LabelControl("RTT Picker Test", osg::Vec4(1,1,0,1)) );
-    s_fidLabel = new ui::LabelControl("---");
-    uiContainer->addControl( s_fidLabel );
-    s_nameLabel = uiContainer->addControl( new ui::LabelControl( "---" ) );
+    uiContainer->addControl( new ui::ButtonControl("Toggle picker", new TogglePicker(app)) );
+    app.fidLabel = new ui::LabelControl("---");
+    uiContainer->addControl( app.fidLabel );
+    app.nameLabel = uiContainer->addControl( new ui::LabelControl( "---" ) );
 
     // Load up the earth file.
-    osg::Node* node = MapNodeHelper().load( arguments, mainView, uiContainer );
+    osg::Node* node = MapNodeHelper().load( arguments, app.mainView, uiContainer );
     if ( node )
     {
-        mainView->setSceneData( node );    
-        mainView->addEventHandler( new TestIsectPicker() );
-
-        // create a picker of the specified size.
-        RTTPicker* picker = new RTTPicker();
-        mainView->addEventHandler( picker );
-
-        // add the graph that will be picked.
-        picker->addChild( MapNode::get(node) );
+        app.mainView->setSceneData( node );
 
-        // install a callback that controls the picker and listens for hits.
-        picker->setDefaultCallback( new MyPickCallback() );
+        app.mapNode = MapNode::get(node);
 
-        // Make a view that lets us see what the picker sees.
-        osgViewer::View* rttView = new osgViewer::View();
-        rttView->getCamera()->setGraphicsContext( mainView->getCamera()->getGraphicsContext() );
-        rttView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
-        viewer.addView( rttView );
-        setupRTTView( rttView, picker->getOrCreateTexture(mainView) );
+        // start with a picker running
+        startPicker(app);
 
         // Hightlight features as we pick'em.
-        installHighlighter(
-            MapNode::get(node)->getOrCreateStateSet(),
-            Registry::objectIndex()->getObjectIDAttribLocation() );
+        installHighlighter(app);
 
-        mainView->getCamera()->setName( "Main view" );
+        app.mainView->getCamera()->setName( "Main view" );
 
-        return viewer.run();
+        return app.viewer.run();
     }
     else
     {
diff --git a/src/applications/osgearth_qt_simple/CMakeLists.txt b/src/applications/osgearth_qt_simple/CMakeLists.txt
index 17f55aa..7b92078 100644
--- a/src/applications/osgearth_qt_simple/CMakeLists.txt
+++ b/src/applications/osgearth_qt_simple/CMakeLists.txt
@@ -14,10 +14,12 @@ IF(Qt5Widgets_FOUND)
         QT5_WRAP_CPP( MOC_SRC ${MOC_HDR} OPTIONS "-f${MOC_HDR_ABS}" )
         LIST( APPEND MOC_SRCS ${MOC_SRC} )
     ENDFOREACH()
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt5)
 ENDIF()
 
 IF(QT4_FOUND)
     QT4_WRAP_CPP( MOC_SRCS ${MOC_HDRS} OPTIONS "-f" )
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt)
 ENDIF()
 
 SET(TARGET_H
@@ -31,11 +33,15 @@ SET(TARGET_SRC
 )
 
 SET(TARGET_ADDED_LIBRARIES
-    osgEarthQt
+    ${OSGEARTH_QT_LIBRARY}
     ${QT_QTCORE_LIBRARY}
     ${QT_QTGUI_LIBRARY}
     ${QT_QTOPENGL_LIBRARY}
 )
 
 #### end var setup  ###
+IF(Qt5Widgets_FOUND)
+SETUP_APPLICATION(osgearth_qt5_simple)
+ELSE(Qt5Widgets_FOUND)
 SETUP_APPLICATION(osgearth_qt_simple)
+ENDIF(Qt5Widgets_FOUND)
diff --git a/src/applications/osgearth_qt_windows/CMakeLists.txt b/src/applications/osgearth_qt_windows/CMakeLists.txt
index 597f6e6..4409994 100644
--- a/src/applications/osgearth_qt_windows/CMakeLists.txt
+++ b/src/applications/osgearth_qt_windows/CMakeLists.txt
@@ -10,9 +10,11 @@ IF(Qt5Widgets_FOUND)
         QT5_WRAP_CPP( MOC_SRC ${MOC_HDR} OPTIONS "-f${MOC_HDR_ABS}" )
         LIST( APPEND MOC_SRCS ${MOC_SRC} )
     ENDFOREACH()
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt5)
 ELSE()
     INCLUDE( ${QT_USE_FILE} )
     QT4_WRAP_CPP( MOC_SRCS ${MOC_HDRS} OPTIONS "-f" )
+    SET(OSGEARTH_QT_LIBRARY osgEarthQt)
 ENDIF()
 
 SET(TARGET_H
@@ -26,11 +28,15 @@ SET(TARGET_SRC
 )
 
 SET(TARGET_ADDED_LIBRARIES
-    osgEarthQt
+    ${OSGEARTH_QT_LIBRARY}
     ${QT_QTCORE_LIBRARY}
     ${QT_QTGUI_LIBRARY}
     ${QT_QTOPENGL_LIBRARY}
 )
 
 #### end var setup  ###
+IF(Qt5Widgets_FOUND)
+SETUP_APPLICATION(osgearth_qt5_windows)
+ELSE(Qt5Widgets_FOUND)
 SETUP_APPLICATION(osgearth_qt_windows)
+ENDIF(Qt5Widgets_FOUND)
diff --git a/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp b/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp
index d4f7a6a..a7c03c3 100644
--- a/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp
+++ b/src/applications/osgearth_qt_windows/osgearth_qt_windows.cpp
@@ -31,6 +31,7 @@
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthQt/ViewWidget>
 #include <osgEarth/Random>
+#include <osgEarth/FileUtils>
 #include <QApplication>
 #include <QDialog>
 #include <QMainWindow>
@@ -147,8 +148,8 @@ main(int argc, char** argv)
         return usage("Help", args);
 
     // load something
-    osg::Node* node = osgDB::readNodeFiles( args );
-    if ( !node )
+    osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles( args );
+    if (!node.valid())
         return usage("Can't load a scene!", args);
 
 
@@ -161,7 +162,7 @@ main(int argc, char** argv)
     QApplication q(argc, argv);
 
     // fire up our controller:
-    MyMainWindow win( args, node );
+    MyMainWindow win( args, node.get() );
 
     // A button for adding new views:
     QPushButton* addButton = new AddButton( &win, "Add a view" );
diff --git a/src/applications/osgearth_seed/osgearth_seed.cpp b/src/applications/osgearth_seed/osgearth_seed.cpp
index 788ed56..314cd15 100644
--- a/src/applications/osgearth_seed/osgearth_seed.cpp
+++ b/src/applications/osgearth_seed/osgearth_seed.cpp
@@ -31,10 +31,15 @@
 #include <osgEarth/CacheSeed>
 #include <osgEarth/MapNode>
 #include <osgEarth/Registry>
-#include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 #include <osgEarth/FileUtils>
-
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
 #include <osgEarth/TileVisitor>
+#include <osgEarth/FileUtils>
+
+#include <osgEarthFeatures/FeatureCursor>
+
+#include <osgEarthDrivers/feature_ogr/OGRFeatureOptions>
 
 #include <iostream>
 #include <sstream>
@@ -108,7 +113,7 @@ int message( const std::string& msg )
 }
 
 int
-    seed( osg::ArgumentParser& args )
+seed( osg::ArgumentParser& args )
 {    
     osgDB::Registry::instance()->getReaderWriterForExtension("png");
     osgDB::Registry::instance()->getReaderWriterForExtension("jpg");
@@ -289,7 +294,7 @@ int
     
     if (verbose)
     {
-        visitor->setProgressCallback( progress );
+        visitor->setProgressCallback( progress.get() );
     }
 
     if ( minLevel >= 0 )
@@ -315,12 +320,12 @@ int
     // They want to seed an image layer
     if (imageLayerIndex >= 0)
     {
-        osg::ref_ptr< ImageLayer > layer = map->getImageLayerAt( imageLayerIndex );
+        osg::ref_ptr< ImageLayer > layer = map->getLayerAt<ImageLayer>( imageLayerIndex );
         if (layer)
         {
             OE_NOTICE << "Seeding single layer " << layer->getName() << std::endl;
             osg::Timer_t start = osg::Timer::instance()->tick();        
-            seeder.run(layer, map);
+            seeder.run(layer.get(), map);
             osg::Timer_t end = osg::Timer::instance()->tick();
             if (verbose)
             {
@@ -337,12 +342,12 @@ int
     // They want to seed an elevation layer
     else if (elevationLayerIndex >= 0)
     {
-        osg::ref_ptr< ElevationLayer > layer = map->getElevationLayerAt( elevationLayerIndex );
+        osg::ref_ptr< ElevationLayer > layer = map->getLayerAt<ElevationLayer>( elevationLayerIndex );
         if (layer)
         {
             OE_NOTICE << "Seeding single layer " << layer->getName() << std::endl;
             osg::Timer_t start = osg::Timer::instance()->tick();        
-            seeder.run(layer, map);
+            seeder.run(layer.get(), map);
             osg::Timer_t end = osg::Timer::instance()->tick();
             if (verbose)
             {
@@ -357,11 +362,14 @@ int
     }
     // They want to seed the entire map
     else
-    {                
+    {
+        TerrainLayerVector terrainLayers;
+        map->getLayers(terrainLayers);
+
         // Seed all the map layers
-        for (unsigned int i = 0; i < map->getNumImageLayers(); ++i)
+        for (unsigned int i = 0; i < terrainLayers.size(); ++i)
         {            
-            osg::ref_ptr< ImageLayer > layer = map->getImageLayerAt(i);
+            osg::ref_ptr< TerrainLayer > layer = terrainLayers[i].get();
             OE_NOTICE << "Seeding layer" << layer->getName() << std::endl;            
             osg::Timer_t start = osg::Timer::instance()->tick();
             seeder.run(layer.get(), map);            
@@ -372,18 +380,18 @@ int
             }                
         }
 
-        for (unsigned int i = 0; i < map->getNumElevationLayers(); ++i)
-        {
-            osg::ref_ptr< ElevationLayer > layer = map->getElevationLayerAt(i);
-            OE_NOTICE << "Seeding layer" << layer->getName() << std::endl;
-            osg::Timer_t start = osg::Timer::instance()->tick();
-            seeder.run(layer.get(), map);            
-            osg::Timer_t end = osg::Timer::instance()->tick();
-            if (verbose)
-            {
-                OE_NOTICE << "Completed seeding layer " << layer->getName() << " in " << prettyPrintTime( osg::Timer::instance()->delta_s( start, end ) ) << std::endl;
-            }                
-        }        
+        //for (unsigned int i = 0; i < map->getNumElevationLayers(); ++i)
+        //{
+        //    osg::ref_ptr< ElevationLayer > layer = map->getElevationLayerAt(i);
+        //    OE_NOTICE << "Seeding layer" << layer->getName() << std::endl;
+        //    osg::Timer_t start = osg::Timer::instance()->tick();
+        //    seeder.run(layer.get(), map);            
+        //    osg::Timer_t end = osg::Timer::instance()->tick();
+        //    if (verbose)
+        //    {
+        //        OE_NOTICE << "Completed seeding layer " << layer->getName() << " in " << prettyPrintTime( osg::Timer::instance()->delta_s( start, end ) ) << std::endl;
+        //    }                
+        //}        
     }    
 
     return 0;
@@ -412,8 +420,9 @@ int list( osg::ArgumentParser& args )
     MapFrame mapf( mapNode->getMap() );
 
     TerrainLayerVector layers;
-    std::copy( mapf.imageLayers().begin(), mapf.imageLayers().end(), std::back_inserter(layers) );
-    std::copy( mapf.elevationLayers().begin(), mapf.elevationLayers().end(), std::back_inserter(layers) );
+    mapf.getLayers(layers);
+    //std::copy( mapf.imageLayers().begin(), mapf.imageLayers().end(), std::back_inserter(layers) );
+    //std::copy( mapf.elevationLayers().begin(), mapf.elevationLayers().end(), std::back_inserter(layers) );
 
     for( TerrainLayerVector::iterator i =layers.begin(); i != layers.end(); ++i )
     {
@@ -471,7 +480,7 @@ purge( osg::ArgumentParser& args )
 
 
     ImageLayerVector imageLayers;
-    map->getImageLayers( imageLayers );
+    map->getLayers( imageLayers );
     for( ImageLayerVector::const_iterator i = imageLayers.begin(); i != imageLayers.end(); ++i )
     {
         ImageLayer* layer = i->get();
@@ -498,7 +507,7 @@ purge( osg::ArgumentParser& args )
     }
 
     ElevationLayerVector elevationLayers;
-    map->getElevationLayers( elevationLayers );
+    map->getLayers( elevationLayers );
     for( ElevationLayerVector::const_iterator i = elevationLayers.begin(); i != elevationLayers.end(); ++i )
     {
         ElevationLayer* layer = i->get();
diff --git a/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp b/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp
index fde3348..f44642f 100644
--- a/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp
+++ b/src/applications/osgearth_sequencecontrol/osgearth_sequencecontrol.cpp
@@ -23,6 +23,7 @@
 #include <osgViewer/Viewer>
 #include <osgEarth/MapNode>
 #include <osgEarth/TimeControl>
+#include <osgEarth/ImageLayer>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/EarthManipulator>
 
@@ -78,7 +79,7 @@ main(int argc, char** argv)
     // find the first layer with sequence control.
     bool found = false;
     osgEarth::ImageLayerVector layers;
-    mapNode->getMap()->getImageLayers( layers );
+    mapNode->getMap()->getLayers( layers );
     for( osgEarth::ImageLayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i )
     {
         // get the sequence control interface for the layer, if there is one.
diff --git a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
index d744fb0..b29b49a 100644
--- a/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
+++ b/src/applications/osgearth_shadercomp/osgearth_shadercomp.cpp
@@ -40,6 +40,7 @@
 #include <osgEarth/Registry>
 #include <osgEarth/Capabilities>
 #include <osgEarth/ShaderUtils>
+#include <osgEarth/FileUtils>
 #include <osgEarthUtil/Controls>
 
 using namespace osgEarth;
@@ -94,7 +95,7 @@ namespace TEST_1
 {
     char s_hazeVertShader[] =
         "#version " GLSL_VERSION_STR "\n"
-        "varying vec3 v_pos; \n"
+        "out vec3 v_pos; \n"
         "void setup_haze(inout vec4 VertexVIEW) \n"
         "{ \n"
         "    v_pos = vec3(VertexVIEW); \n"
@@ -102,7 +103,7 @@ namespace TEST_1
 
     char s_hazeFragShader[] =
         "#version " GLSL_VERSION_STR "\n"
-        "varying vec3 v_pos; \n"
+        "in vec3 v_pos; \n"
         "void apply_haze(inout vec4 color) \n"
         "{ \n"
         "    float dist = clamp( length(v_pos)/1e7, 0.0, 0.75 ); \n"
@@ -457,8 +458,8 @@ namespace TEST_8
         OE_NOTICE << "Wrote to out.osgt" << std::endl;
 
         node = 0L;
-        node = osgDB::readNodeFile("out.osgt");
-        if (!node) {
+        node = osgDB::readRefNodeFile("out.osgt");
+        if (!node.valid()) {
             OE_WARN << "Readback failed!!" << std::endl;
             exit(0);
         }
@@ -621,35 +622,35 @@ int main(int argc, char** argv)
 
     if ( test1 || test2 || test3 || test4 || test6 )
     {
-        osg::Node* earthNode = osgDB::readNodeFile( "gdal_tiff.earth" );
-        if (!earthNode)
+        osg::ref_ptr<osg::Node> earthNode = osgDB::readRefNodeFile( "simple.earth" );
+        if (!earthNode.valid())
         {
             return usage( "Unable to load earth model." );
         }
 
         if ( test1 )
         {
-            root->addChild( TEST_1::run(earthNode) );
+            root->addChild( TEST_1::run(earthNode.get()) );
             if (ui) label->setText( "Function injection test: the map appears hazy at high altitude." );
         }
         else if ( test2 )
         {
-            root->addChild( TEST_2::run(earthNode) );
+            root->addChild( TEST_2::run(earthNode.get()) );
             if (ui) label->setText( "Accept callback test: the map turns red when viewport width > 1000" );
         }
         else if ( test3 )
         {
-            root->addChild( TEST_3::run(earthNode) );
+            root->addChild( TEST_3::run(earthNode.get()) );
             if (ui) label->setText( "Shader LOD test: the map turns red between 500K and 1M meters altitude" );
         }
         else if ( test4 )
         {
-            root->addChild( TEST_4::run(earthNode) );
+            root->addChild( TEST_4::run(earthNode.get()) );
             if (ui) label->setText("Memory management test; monitor memory for stability");
         }
         else if ( test6 )
         {
-            root->addChild( TEST_6::run(earthNode) );
+            root->addChild( TEST_6::run(earthNode.get()) );
             if (ui) label->setText("State Memory Stack test; top row, both=blue. bottom left=red, bottom right=normal.");
         }
         
@@ -673,13 +674,13 @@ int main(int argc, char** argv)
     }
     else if (test9)
     {
-        osg::Node* earthNode = osgDB::readNodeFile( "readymap.earth" );
-        if (!earthNode)
+        osg::ref_ptr<osg::Node> earthNode = osgDB::readRefNodeFile( "readymap.earth" );
+        if (!earthNode.valid())
         {
             return usage( "Unable to load earth model." );
         }
         
-        root->addChild(TEST_9::run(earthNode));
+        root->addChild(TEST_9::run(earthNode.get()));
         if (ui) label->setText("DP Shader Test - see code comments");
         viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
     }
diff --git a/src/applications/osgearth_shadergen/osgearth_shadergen.cpp b/src/applications/osgearth_shadergen/osgearth_shadergen.cpp
index 9960fce..444c3f1 100644
--- a/src/applications/osgearth_shadergen/osgearth_shadergen.cpp
+++ b/src/applications/osgearth_shadergen/osgearth_shadergen.cpp
@@ -58,10 +58,10 @@ main(int argc, char** argv)
     cache->setMaxSize(INT_MAX);
 
     // load the file
-    osg::Node* node = osgDB::readNodeFile(
+    osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(
         Stringify() << argv[1] << ".osgearth_shadergen" );
 
-    if ( !node )
+    if (!node.valid())
         return usage(argv[0]);
 
     if ( cache )
@@ -75,7 +75,7 @@ main(int argc, char** argv)
         osgUtil::Optimizer::VERTEX_POSTTRANSFORM );
 #endif
 
-    viewer.setSceneData( node );
+    viewer.setSceneData( node.get() );
     viewer.addEventHandler(new osgViewer::StatsHandler());
     viewer.addEventHandler(new osgViewer::WindowSizeHandler());
     viewer.addEventHandler(new osgViewer::ThreadingHandler());
diff --git a/src/applications/osgearth_sharedlayer/osgearth_sharedlayer.cpp b/src/applications/osgearth_sharedlayer/osgearth_sharedlayer.cpp
index 02a6dce..7e2b5af 100644
--- a/src/applications/osgearth_sharedlayer/osgearth_sharedlayer.cpp
+++ b/src/applications/osgearth_sharedlayer/osgearth_sharedlayer.cpp
@@ -86,7 +86,7 @@ public:
         // Make a vertex shader that will access the texture coordinates
         // for our shared layer.
         std::string vs = Stringify()
-            << "varying vec4 mask_layer_texc; \n"
+            << "out vec4 mask_layer_texc; \n"
             << "void my_filter_vertex(inout vec4 VertexMODEL) \n"
             << "{ \n"
             << "    mask_layer_texc = gl_MultiTexCoord" << unit << "; \n"
@@ -97,10 +97,10 @@ public:
         // the shared "mask" layer exceed a certain alpha value.
         std::string fs =
             "uniform sampler2D mask_layer_tex; \n"
-            "varying vec4 mask_layer_texc; \n"
+            "in vec4 mask_layer_texc; \n"
             "void my_color_filter(inout vec4 color) \n"
             "{ \n"
-            "    vec4 mask_texel = texture2D(mask_layer_tex, mask_layer_texc.st); \n"
+            "    vec4 mask_texel = texture(mask_layer_tex, mask_layer_texc.st); \n"
             "    if ( mask_texel.a >= 0.5 ) \n"
             "    { \n"
             "        color.r = 1.0; \n"
@@ -155,8 +155,8 @@ int main(int argc, char** argv)
 
     // create a new map and add our two layers.
     MapNode* mapnode = new MapNode();
-    mapnode->getMap()->addImageLayer( imagery );
-    mapnode->getMap()->addImageLayer( sharedLayer );
+    mapnode->getMap()->addLayer( imagery );
+    mapnode->getMap()->addLayer( sharedLayer );
 
     // make a custom color-filter shader that will modulate the imagery
     // using the texture from the shared layer. (Using a ColorFilter 
diff --git a/src/applications/osgearth_silverlining/osgearth_silverlining.cpp b/src/applications/osgearth_silverlining/osgearth_silverlining.cpp
index f808e00..0696029 100644
--- a/src/applications/osgearth_silverlining/osgearth_silverlining.cpp
+++ b/src/applications/osgearth_silverlining/osgearth_silverlining.cpp
@@ -22,6 +22,7 @@
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
 #include <osgEarthSilverLining/SilverLiningNode>
+#include <osgEarth/NodeUtils>
 
 #define LC "[osgearth_silverlining] "
 
diff --git a/src/applications/osgearth_splat/osgearth_splat.cpp b/src/applications/osgearth_splat/osgearth_splat.cpp
index e429cde..beb50e1 100644
--- a/src/applications/osgearth_splat/osgearth_splat.cpp
+++ b/src/applications/osgearth_splat/osgearth_splat.cpp
@@ -21,115 +21,194 @@
 */
 
 #include <osgViewer/Viewer>
-#include <osgEarth/Notify>
-#include <osgEarth/VirtualProgram>
-#include <osgEarthUtil/EarthManipulator>
+
+#include <osgEarth/MapNode>
+#include <osgEarth/Registry>
+#include <osgEarth/LandCoverLayer>
+
+#include <osgEarthSplat/SplatLayer>
+#include <osgEarthSplat/GroundCoverLayer>
+
+#include <osgEarthDrivers/gdal/GDALOptions>
+
 #include <osgEarthUtil/ExampleResources>
+#include <osgEarthUtil/EarthManipulator>
 
-#include <osgEarthSplat/LandCoverTerrainEffect>
-#include <osgEarthSplat/SplatExtension>
+#include <osgEarthSymbology/BillboardSymbol>
 
-#define LC "[viewer] "
+
+#define LC "[splat] "
 
 using namespace osgEarth;
 using namespace osgEarth::Util;
 using namespace osgEarth::Splat;
+using namespace osgEarth::Drivers;
+using namespace osgEarth::Symbology;
 
 int
-usage(const char* name)
-{
-    OE_NOTICE 
-        << "\nUsage: " << name << " file.earth" << std::endl
-        << MapNodeHelper().usage() << std::endl;
-
-    return 0;
+failed(const std::string& s) {
+    OE_WARN << "FAILED: " << s << "\n";
+    return -1;
 }
 
-const char* color_landcover =
-    "#version 120\n"
-    "void color_landcover(inout vec4 color) \n"
-    "{ \n"
-    "    color = vec4(1,0,0,1);\n"
-    "} \n";
-
 int
 main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
+    bool fromXML = arguments.find("--xml") >= 0;
 
-    // help?
-    if ( arguments.read("--help") )
-        return usage(argv[0]);
-
-    // create a viewer:
-    osgViewer::Viewer viewer(arguments);
-
-    // Tell the database pager to not modify the unref settings
-    viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
-
-    // install our default manipulator (do this before calling load)
-    viewer.setCameraManipulator( new EarthManipulator(arguments) );
-
-    // disable the small-feature culling
-    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
-
-    // set a near/far ratio that is smaller than the default. This allows us to get
-    // closer to the ground without near clipping. If you need more, use --logdepth
-    viewer.getCamera()->setNearFarRatio(0.0001);
+    // Create a land cover dictionary.
+    LandCoverDictionary* dictionary;
 
-    // load an earth file, and support all or our example command-line options
-    // and earth file <external> tags    
-    osg::Node* node = MapNodeHelper().load( arguments, &viewer );
-    if ( node )
+    if (fromXML)
     {
+        LandCoverDictionaryOptions options;
+        if (options.loadFromXML("../data/land_cover_dictionary.xml") == false)
+            return failed("Cannot find XML land cover dictionary");
+        
+        dictionary = new LandCoverDictionary(options);
+    }
+    else
+    {
+        dictionary = new LandCoverDictionary();
+        dictionary->setName("Land Cover Dictionary");
+        dictionary->addClass("forest");
+        dictionary->addClass("cropland");
+        dictionary->addClass("grassland");
+        dictionary->addClass("savanna");
+        dictionary->addClass("swamp");
+        dictionary->addClass("desert");
+        dictionary->addClass("rock");
+        dictionary->addClass("water");    
+        dictionary->addClass("tundra");
+        dictionary->addClass("urban");
+    }
 
-        // Get the MapNode
-        MapNode* mapNode = MapNode::findMapNode( node );
-
-        // Find the Splat Extension
-        SplatExtension* splatExtension = mapNode->getExtension<SplatExtension>();
-        if (splatExtension)
-        {
-            OE_NOTICE << "Found Splat Extension" << std::endl;
-        }
-
-        LandCoverTerrainEffect* landCoverEffect = mapNode->getTerrainEngine()->getEffect<LandCoverTerrainEffect>();
-        if (landCoverEffect)
-        {
-            OE_NOTICE << "Found landcover terrain effect" << std::endl;
-
-            for (Zones::const_iterator zoneItr = landCoverEffect->getZones().begin();
-                zoneItr != landCoverEffect->getZones().end();
-                ++zoneItr)
-            {
-                // Get the StateSet for each of the LandCoverLayers
-                for (LandCoverLayers::iterator landCoverItr = zoneItr->get()->getLandCover()->getLayers().begin();
-                    landCoverItr != zoneItr->get()->getLandCover()->getLayers().end();
-                    ++landCoverItr)
-                {
-                    // Get the stateset for the layer.
-                    osg::StateSet* stateset = landCoverItr->get()->getOrCreateStateSet();
-
-                    // Get the VirtualProgram for this layer.
-                    VirtualProgram* vp = VirtualProgram::getOrCreate(stateset);
-
-                    // Make the "tree" layer all red.
-                    if (landCoverItr->get()->getName() == "trees")
-                    {                                         
-                        vp->setFunction( "color_landcover", color_landcover, ShaderComp::LOCATION_FRAGMENT_LIGHTING);
-                    }
-                }
-            }
-        }
-
-        viewer.setSceneData( node );
-        while(!viewer.done())
-        {
-            viewer.frame();
-        }
+    // Create the data source for our land cover data and
+    // map each value to a class in the dictionary.
+    // This example uses the ESA GLOBCOVER data set from
+    // http://due.esrin.esa.int/page_globcover.php
+    GDALOptions coverageDriver;
+    coverageDriver.url() = "H:/data/esa/GLOBCOVER_L4_200901_200912_V2.3_Ant_tiled.tif"; 
+    coverageDriver.profile() = ProfileOptions("global-geodetic");
+
+    LandCoverCoverageLayerOptions coverage;
+    coverage.driver() = coverageDriver;
+    coverage.warp() = 0.035;
+    if (fromXML)
+    {
+        if (coverage.loadMappingsFromXML("../data/land_cover_ESA_GLOBCOVER.xml") == false)
+            return failed("Cannot find coverage mappings XML\n");
     }
     else
     {
-        return usage(argv[0]);
+        coverage.map(11, "cropland");
+        coverage.map(14, "cropland");
+        coverage.map(20, "cropland");
+        coverage.map(30, "cropland");
+        coverage.map(40, "forest");
+        coverage.map(50, "forest");
+        coverage.map(60, "forest");
+        coverage.map(70, "forest");
+        coverage.map(80, "forest");
+        coverage.map(90, "forest");
+        coverage.map(100, "forest");
+        coverage.map(110, "grassland");
+        coverage.map(120, "grassland");
+        coverage.map(130, "savanna");
+        coverage.map(140, "savanna");
+        coverage.map(150, "savanna");
+        coverage.map(160, "swamp");
+        coverage.map(170, "swamp");
+        coverage.map(180, "swamp");
+        coverage.map(190, "urban");
+        coverage.map(200, "desert");
+        coverage.map(210, "water");
+        coverage.map(220, "tundra");
+        coverage.map(230, "water");
     }
+
+    // Create the land cover layer for the map:
+    LandCoverLayer* landCover = new LandCoverLayer();
+    landCover->setName("LandCover");
+    landCover->options().cachePolicy() = CachePolicy::NO_CACHE;
+    landCover->options().coverages().push_back(coverage);
+    landCover->options().maxDataLevel() = 15u;
+
+    // Next, load the definitions that map land cover classes to actual textures.
+    Surface* surface = new Surface();
+    SplatCatalog* catalog = SplatCatalog::read("../data/splat/splat_catalog.xml");
+    if (catalog == 0L)
+        return failed("Reading splat catalog");
+    surface->setCatalog(catalog);
+
+    // The zone designates the geographic area over which to apply the surface.
+    // At least one zone is required and by default it covers the entire map.
+    Zone* splatZone = new Zone();
+    splatZone->setSurface(surface);
+    
+    // Create an imagery splatting layer that uses the configured land cover.
+    SplatLayer* splatLayer = new SplatLayer();
+    splatLayer->setName("Splat imagery");
+    splatLayer->options().cachePolicy() = CachePolicy::NO_CACHE;
+    splatLayer->setLandCoverDictionary(dictionary);
+    splatLayer->setLandCoverLayer(landCover);
+    splatLayer->zones().push_back(splatZone);
+
+
+    // Now, the trees:
+
+    // Load a tree image and make a billboard symbol from it:
+    osg::ref_ptr<osg::Image> tree = URI("../data/splat/pine2.png").getImage();
+    if (tree.valid() == false)
+        return failed("Loading tree image");
+
+    BillboardSymbol* treeSymbol = new BillboardSymbol();
+    treeSymbol->setImage(tree.get());
+    treeSymbol->width() = 12.0f;
+    treeSymbol->height() = 16.0f;
+
+    // Add this symbol to a "frest" biome.
+    GroundCoverBiomeOptions forestBiome;
+    forestBiome.biomeClasses() = "forest";
+    forestBiome.symbols().push_back(treeSymbol);
+    
+    // Assemble the ground cover coniguration:
+    GroundCoverOptions treeOptions;
+    treeOptions.biomes().push_back(forestBiome);
+    treeOptions.maxDistance() = 15000.0;
+    treeOptions.density() = 4.0;
+    treeOptions.fill() = 0.85;
+    treeOptions.brightness() = 2.0;
+    treeOptions.contrast() = 1.0;
+    GroundCover* trees = new GroundCover(treeOptions);
+
+    Zone* treeZone = new Zone();
+    treeZone->setGroundCover(trees);
+
+    // Now, create a ground cover layer for some trees.
+    GroundCoverLayer* treeLayer = new GroundCoverLayer();
+    treeLayer->setName("Ground cover");
+    treeLayer->options().lod() = 13;
+    treeLayer->setLandCoverDictionary(dictionary);
+    treeLayer->setLandCoverLayer(landCover);
+    treeLayer->zones().push_back(treeZone);
+
+
+    // Assemble the Map.
+    Map* map = new Map();
+    map->addLayer(dictionary);
+    map->addLayer(landCover);
+    map->addLayer(splatLayer);
+    map->addLayer(treeLayer);
+
+    // Activate the REX terrain engine (required for splatting)
+    osgEarth::Registry::instance()->overrideTerrainEngineDriverName() = "rex";
+
+    // create a viewer:
+    osgViewer::Viewer viewer(arguments);
+    viewer.getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
+    viewer.setCameraManipulator( new EarthManipulator(arguments) );
+    viewer.setSceneData(new MapNode(map));
+    return viewer.run();
 }
diff --git a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
index 9c80a95..399f798 100644
--- a/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
+++ b/src/applications/osgearth_terrainprofile/osgearth_terrainprofile.cpp
@@ -32,6 +32,7 @@
 #include <osgEarthUtil/TerrainProfile>
 #include <osgEarth/GeoMath>
 #include <osgEarth/Registry>
+#include <osgEarth/FileUtils>
 #include <osgEarthFeatures/Feature>
 #include <osgEarthAnnotation/FeatureNode>
 #include <osgText/Text>
@@ -348,8 +349,8 @@ main(int argc, char** argv)
     osgViewer::Viewer viewer(arguments);
 
     // load the .earth file from the command line.
-    osg::Node* earthNode = osgDB::readNodeFiles( arguments );
-    if (!earthNode)
+    osg::ref_ptr<osg::Node> earthNode = osgDB::readNodeFiles( arguments );
+    if (!earthNode.valid())
     {
         OE_NOTICE << "Unable to load earth model" << std::endl;
         return 1;
@@ -357,7 +358,7 @@ main(int argc, char** argv)
 
     osg::Group* root = new osg::Group();
 
-    osgEarth::MapNode * mapNode = osgEarth::MapNode::findMapNode( earthNode );
+    osgEarth::MapNode * mapNode = osgEarth::MapNode::findMapNode( earthNode.get() );
     if (!mapNode)
     {
         OE_NOTICE << "Could not find MapNode " << std::endl;
@@ -390,7 +391,7 @@ main(int argc, char** argv)
 
     viewer.getCamera()->addCullCallback( new AutoClipPlaneCullCallback(mapNode));
 
-    viewer.addEventHandler( new DrawProfileEventHandler( mapNode, root, calculator ) );
+    viewer.addEventHandler( new DrawProfileEventHandler( mapNode, root, calculator.get() ) );
 
     viewer.setSceneData( root );    
 
diff --git a/src/applications/osgearth_tfs/osgearth_tfs.cpp b/src/applications/osgearth_tfs/osgearth_tfs.cpp
index 45bd00f..f28fc1a 100644
--- a/src/applications/osgearth_tfs/osgearth_tfs.cpp
+++ b/src/applications/osgearth_tfs/osgearth_tfs.cpp
@@ -236,7 +236,7 @@ int main(int argc, char** argv)
     packager.setDestSRS( destSRS );
     packager.setLod0Extent(ext);
 
-    packager.package( features, destination, layer, description );
+    packager.package( features.get(), destination, layer, description );
     osg::Timer_t endTime = osg::Timer::instance()->tick();
     OE_NOTICE << "Completed in " << osg::Timer::instance()->delta_s( startTime, endTime ) << " s " << std::endl;
 
diff --git a/src/applications/osgearth_tilesource/osgearth_tilesource.cpp b/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
index d583b8b..aa3ae1a 100644
--- a/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
+++ b/src/applications/osgearth_tilesource/osgearth_tilesource.cpp
@@ -25,6 +25,7 @@
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/Map>
 #include <osgEarth/MapNode>
+#include <osgEarth/ImageLayer>
 #include <osgEarth/Registry>
 #include <osgEarthSymbology/Geometry>
 #include <osgEarthSymbology/GeometryRasterizer>
@@ -105,7 +106,7 @@ int main(int argc, char** argv)
     ImageLayerOptions options( "custom" );
     CustomTileSource* tileSource = new CustomTileSource();
     tileSource->open();
-    map->addImageLayer( new ImageLayer(options, tileSource) );
+    map->addLayer( new ImageLayer(options, tileSource) );
 
     // That's it, the map is ready; now create a MapNode to render the Map:
     MapNode* mapNode = new MapNode( map );
diff --git a/src/applications/osgearth_toc/osgearth_toc.cpp b/src/applications/osgearth_toc/osgearth_toc.cpp
index 6f07547..bd54a86 100644
--- a/src/applications/osgearth_toc/osgearth_toc.cpp
+++ b/src/applications/osgearth_toc/osgearth_toc.cpp
@@ -23,36 +23,45 @@
 #include <osgEarth/MapFrame>
 #include <osgEarth/MapNode>
 #include <osgEarth/MapModelChange>
+#include <osgEarth/ElevationPool>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/Controls>
 #include <osgEarthUtil/ExampleResources>
+#include <osgEarthUtil/ViewFitter>
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgGA/StateSetManipulator>
 #include <osgDB/ReadFile>
 
 using namespace osgEarth;
+using namespace osgEarth::Util;
 using namespace osgEarth::Util::Controls;
 
 void createControlPanel( osgViewer::View* );
 void updateControlPanel();
 
 static osg::ref_ptr<Map> s_activeMap;
-static osg::ref_ptr<Map> s_inactiveMap;
 static Grid* s_masterGrid;
-static Grid* s_imageBox;
-static Grid* s_elevationBox;
-static Grid* s_modelBox;
+static Grid* s_activeBox;
+static Grid* s_inactiveBox;
 static bool s_updateRequired = true;
-static MapModelChange::ActionType s_changeAction;
+static MapModelChange s_change;
+static EarthManipulator* s_manip;
+static osgViewer::View* s_view;
+
+typedef std::map<std::string, ConfigOptions> InactiveLayers;
+static InactiveLayers _inactive;
 
 //------------------------------------------------------------------------
 
+void updateControlPanel();
+
+
 struct MyMapListener : public MapCallback
 {
     void onMapModelChanged( const MapModelChange& change ) {
         s_updateRequired = true;
-        s_changeAction = change.getAction();
+        s_change = change;
     }
 };
 
@@ -67,8 +76,7 @@ struct UpdateOperation : public osg::Operation
             updateControlPanel();
             s_updateRequired = false;
 
-            if (s_changeAction == MapModelChange::ADD_ELEVATION_LAYER ||
-                s_changeAction == MapModelChange::REMOVE_ELEVATION_LAYER)
+            if (s_change.getElevationLayer())
             {
                 OE_NOTICE << "Dirtying model layers.\n";
                 dirtyModelLayers();
@@ -78,22 +86,47 @@ struct UpdateOperation : public osg::Operation
 
     void dirtyModelLayers()
     {
-        for(unsigned i=0; i<s_activeMap->getNumModelLayers(); ++i)
+        ModelLayerVector modelLayers;
+        s_activeMap->getLayers(modelLayers);
+
+        for(unsigned i=0; i<modelLayers.size(); ++i)
         {
-            ModelSource* ms = s_activeMap->getModelLayerAt(i)->getModelSource();
+            ModelSource* ms = modelLayers[i]->getModelSource();
             if ( ms )
             {
                 ms->dirty();
             }
             else
             {
-                OE_NOTICE << s_activeMap->getModelLayerAt(i)->getName()
+                OE_NOTICE << modelLayers[i]->getName()
                     << " has no model source.\n";
             }
         }
     }
 };
 
+
+struct DumpElevation : public osgGA::GUIEventHandler
+{
+    DumpElevation(MapNode* mapNode, char c) : _mapNode(mapNode), _c(c) { }
+    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor*)
+    {
+        if (ea.getEventType() == ea.KEYDOWN && ea.getKey() == _c)
+        {
+            osg::Vec3d world;
+            _mapNode->getTerrain()->getWorldCoordsUnderMouse(aa.asView(), ea.getX(), ea.getY(), world);
+            GeoPoint coords;
+            coords.fromWorld(s_activeMap->getSRS(), world);
+            osg::ref_ptr<ElevationEnvelope> env = s_activeMap->getElevationPool()->createEnvelope(s_activeMap->getSRS(), 23u);
+            float ep_elev = env->getElevation(coords.x(), coords.y());
+            OE_NOTICE << "Elevations under mouse. EP=" << ep_elev << "\n";
+        }
+        return false;
+    }
+    char _c;
+    MapNode* _mapNode;
+};
+
 //------------------------------------------------------------------------
 
 int
@@ -103,9 +136,13 @@ main( int argc, char** argv )
 
     // configure the viewer.
     osgViewer::Viewer viewer( arguments );
+    s_view = &viewer;
 
     // install a motion model
-    viewer.setCameraManipulator( new osgEarth::Util::EarthManipulator() );
+    viewer.setCameraManipulator( s_manip = new osgEarth::Util::EarthManipulator() );
+
+    // disable the small-feature culling (so text will work)
+    viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
 
     // Load an earth file 
     osg::Node* loaded = osgEarth::Util::MapNodeHelper().load(arguments, &viewer);
@@ -119,10 +156,6 @@ main( int argc, char** argv )
     s_activeMap = mapNode->getMap();
     s_activeMap->addMapCallback( new MyMapListener() );
 
-    // a Map to hold inactive layers (layers that have been removed from the displayed Map)
-    s_inactiveMap = new Map();
-    s_inactiveMap->addMapCallback( new MyMapListener() );
-
     osg::Group* root = new osg::Group();
 
     // install the control panel
@@ -137,112 +170,93 @@ main( int argc, char** argv )
     // install our control panel updater
     viewer.addUpdateOperation( new UpdateOperation() );
 
+    viewer.addEventHandler(new DumpElevation(mapNode, 'E'));
+
     viewer.run();
 }
 
 //------------------------------------------------------------------------
 
-struct LayerVisibleHandler : public ControlEventHandler
+struct ToggleLayerVisibility : public ControlEventHandler
 {
-    LayerVisibleHandler( TerrainLayer* layer ) : _layer(layer) { }
+    ToggleLayerVisibility( VisibleLayer* layer ) : _layer(layer) { }
     void onValueChanged( Control* control, bool value )
     {
         _layer->setVisible( value );
     }
-    TerrainLayer* _layer;
+    VisibleLayer* _layer;
 };
 
 struct LayerOpacityHandler : public ControlEventHandler
 {
-    LayerOpacityHandler( ImageLayer* layer ) : _layer(layer) { }
+    LayerOpacityHandler( VisibleLayer* layer ) : _layer(layer) { }
     void onValueChanged( Control* control, float value )
     {
         _layer->setOpacity( value );
     }
-    ImageLayer* _layer;
-};
-
-struct ModelLayerVisibleHandler : public ControlEventHandler
-{
-    ModelLayerVisibleHandler( ModelLayer* layer ) : _layer(layer) { }
-    void onValueChanged( Control* control, bool value )
-    {
-        _layer->setVisible( value );
-    }
-    ModelLayer* _layer;
-};
-
-struct ModelLayerOpacityHandler : public ControlEventHandler
-{
-    ModelLayerOpacityHandler( ModelLayer* layer ) : _layer(layer) { }
-    void onValueChanged( Control* control, float value )
-    {
-        _layer->setOpacity( value );
-    }
-    ModelLayer* _layer;
+    VisibleLayer* _layer;
 };
 
 struct AddLayerHandler : public ControlEventHandler
 {
-    AddLayerHandler( TerrainLayer* layer ) : _layer(layer) { }
-    void onClick( Control* control, int mouseButtonMask ) {
+    AddLayerHandler(const ConfigOptions& lc) : _lc(lc) { }
 
-        ImageLayer* imageLayer = dynamic_cast< ImageLayer*>( _layer.get() );
-        ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer*>( _layer.get() );
-
-        if (imageLayer)
-        {
-            s_inactiveMap->removeImageLayer( imageLayer );
-            s_activeMap->addImageLayer( imageLayer );
-        }
-        else
+    void onClick( Control* control, int mouseButtonMask )
+    {
+        Layer* layer = Layer::create(_lc);
+        if (layer)
         {
-            s_inactiveMap->removeElevationLayer( elevationLayer );
-            s_activeMap->addElevationLayer( elevationLayer );
+            s_activeMap->addLayer(layer);
+            _inactive.erase(layer->getName());
         }
     }
-    osg::ref_ptr<TerrainLayer> _layer;
+
+    ConfigOptions _lc;
 };
 
 struct RemoveLayerHandler : public ControlEventHandler
 {
-    RemoveLayerHandler( TerrainLayer* layer ) : _layer(layer) { }
-    void onClick( Control* control, int mouseButtonMask ) {        
-        ImageLayer* imageLayer = dynamic_cast< ImageLayer*>( _layer.get() );
-        ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer*>( _layer.get() );
+    RemoveLayerHandler( Layer* layer ) : _layer(layer) { }
 
-        if (imageLayer)
-        {
-            s_inactiveMap->addImageLayer( imageLayer );
-            s_activeMap->removeImageLayer( imageLayer );
-        }
-        else
-        {
-            s_inactiveMap->addElevationLayer( elevationLayer );
-            s_activeMap->removeElevationLayer( elevationLayer );
-        }
+    void onClick( Control* control, int mouseButtonMask )
+    {
+        _inactive[_layer->getName()] = _layer->getConfig(); // save it
+        s_activeMap->removeLayer(_layer.get()); // and remove it
     }
-    osg::ref_ptr<TerrainLayer> _layer;
+    osg::ref_ptr<Layer> _layer;
 };
 
 struct MoveLayerHandler : public ControlEventHandler
 {
-    MoveLayerHandler( TerrainLayer* layer, int newIndex ) : _layer(layer), _newIndex(newIndex) { }
-    void onClick( Control* control, int mouseButtonMask ) {
-        ImageLayer* imageLayer = dynamic_cast< ImageLayer*>( _layer );
-        ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer*>( _layer );
+    MoveLayerHandler( Layer* layer, int newIndex ) : _layer(layer), _newIndex(newIndex) { }
+    void onClick( Control* control, int mouseButtonMask )
+    {
+        s_activeMap->moveLayer(_layer, _newIndex);
+    }
+    Layer* _layer;
+    int _newIndex;
+};
 
-        if (imageLayer)
-        {
-            s_activeMap->moveImageLayer( imageLayer, _newIndex );
-        }
-        else
+struct ZoomLayerHandler : public ControlEventHandler
+{
+    ZoomLayerHandler(Layer* layer) : _layer(layer) { }
+    void onClick(Control* control)
+    {
+        const GeoExtent& extent = _layer->getExtent();
+        if (extent.isValid())
         {
-            s_activeMap->moveElevationLayer( elevationLayer, _newIndex );
+            ViewFitter fitter(s_activeMap->getSRS(), s_view->getCamera());
+            std::vector<GeoPoint> points;
+            points.push_back(GeoPoint(extent.getSRS(), extent.west(), extent.south()));
+            points.push_back(GeoPoint(extent.getSRS(), extent.east(), extent.north()));
+            Viewpoint vp;
+            if (fitter.createViewpoint(points, vp))
+            {
+                s_manip->setViewpoint(vp, 2.0);
+            }
         }
     }
-    TerrainLayer* _layer;
-    int _newIndex;
+    Layer* _layer;
 };
 
 //------------------------------------------------------------------------
@@ -254,82 +268,109 @@ createControlPanel( osgViewer::View* view )
     ControlCanvas* canvas = ControlCanvas::getOrCreate( view );
 
     s_masterGrid = new Grid();
-    s_masterGrid->setBackColor(0,0,0,0.5);
-    s_masterGrid->setMargin( 10 );
-    s_masterGrid->setPadding( 10 );
+    s_masterGrid->setMargin( 5 );
+    s_masterGrid->setPadding( 5 );
     s_masterGrid->setChildSpacing( 10 );
     s_masterGrid->setChildVertAlign( Control::ALIGN_CENTER );
     s_masterGrid->setAbsorbEvents( true );
     s_masterGrid->setVertAlign( Control::ALIGN_TOP );
 
-    //The image layers
-    s_imageBox = new Grid();
-    s_imageBox->setBackColor(0,0,0,0.5);
-    s_imageBox->setMargin( 10 );
-    s_imageBox->setPadding( 10 );
-    s_imageBox->setChildSpacing( 10 );
-    s_imageBox->setChildVertAlign( Control::ALIGN_CENTER );
-    s_imageBox->setAbsorbEvents( true );
-    s_imageBox->setVertAlign( Control::ALIGN_TOP );
-    s_masterGrid->setControl( 0, 0, s_imageBox );
-
-    //the elevation layers
-    s_elevationBox = new Grid();
-    s_elevationBox->setBackColor(0,0,0,0.5);
-    s_elevationBox->setMargin( 10 );
-    s_elevationBox->setPadding( 10 );
-    s_elevationBox->setChildSpacing( 10 );
-    s_elevationBox->setChildVertAlign( Control::ALIGN_CENTER );
-    s_elevationBox->setAbsorbEvents( true );
-    s_elevationBox->setVertAlign( Control::ALIGN_TOP );
-    s_masterGrid->setControl( 1, 0, s_elevationBox );
-
-    //The image layers
-    s_modelBox = new Grid();
-    s_modelBox->setBackColor(0,0,0,0.5);
-    s_modelBox->setMargin( 10 );
-    s_modelBox->setPadding( 10 );
-    s_modelBox->setChildSpacing( 10 );
-    s_modelBox->setChildVertAlign( Control::ALIGN_CENTER );
-    s_modelBox->setAbsorbEvents( true );
-    s_modelBox->setVertAlign( Control::ALIGN_TOP );
-    s_masterGrid->setControl( 2, 0, s_modelBox );
+    //The Map layers
+    s_activeBox = new Grid();
+    s_activeBox->setBackColor(0,0,0,0.5);
+    s_activeBox->setMargin( 10 );
+    s_activeBox->setPadding( 10 );
+    s_activeBox->setChildSpacing( 10 );
+    s_activeBox->setChildVertAlign( Control::ALIGN_CENTER );
+    s_activeBox->setAbsorbEvents( true );
+    s_activeBox->setVertAlign( Control::ALIGN_TOP );
+    s_masterGrid->setControl( 0, 0, s_activeBox );
+
+    //the removed layers
+    s_inactiveBox = new Grid();
+    s_inactiveBox->setBackColor(0,0,0,0.5);
+    s_inactiveBox->setMargin( 10 );
+    s_inactiveBox->setPadding( 10 );
+    s_inactiveBox->setChildSpacing( 10 );
+    s_inactiveBox->setChildVertAlign( Control::ALIGN_CENTER );
+    s_inactiveBox->setAbsorbEvents( true );
+    s_inactiveBox->setVertAlign( Control::ALIGN_TOP );
+    s_masterGrid->setControl( 0, 1, s_inactiveBox );
 
     canvas->addControl( s_masterGrid );
 }
 
 void
-createLayerItem( Grid* grid, int gridRow, int layerIndex, int numLayers, TerrainLayer* layer, bool isActive )
+addLayerItem( Grid* grid, int layerIndex, int numLayers, Layer* layer, bool isActive )
 {
     int gridCol = 0;
+    int gridRow = grid->getNumRows();
+
+    VisibleLayer* visibleLayer = dynamic_cast<VisibleLayer*>(layer);
+
+    // only show layers that derive from VisibleLayer
+    if (!visibleLayer)
+        return;
+
+    ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(layer);
+
+    // don't show hidden coverage layers
+    if (imageLayer && imageLayer->isCoverage() && !imageLayer->getVisible())
+        return;
+    
+    ElevationLayer* elevationLayer = dynamic_cast<ElevationLayer*>(layer);
 
     // a checkbox to enable/disable the layer:
-    CheckBoxControl* enabled = new CheckBoxControl( layer->getVisible() );
-    enabled->addEventHandler( new LayerVisibleHandler(layer) );
-    grid->setControl( gridCol++, gridRow, enabled );
+    if (visibleLayer && layer->getEnabled() && !(imageLayer && imageLayer->isCoverage()))
+    {
+        CheckBoxControl* enabled = new CheckBoxControl( visibleLayer->getVisible() );
+        enabled->addEventHandler( new ToggleLayerVisibility(visibleLayer) );
+        grid->setControl( gridCol, gridRow, enabled );
+    }
+    gridCol++;
 
     // the layer name
     LabelControl* name = new LabelControl( layer->getName() );
+    if (!layer->getEnabled())
+        name->setForeColor(osg::Vec4f(1,1,1,0.35));
     grid->setControl( gridCol, gridRow, name );
     gridCol++;
 
-    ImageLayer* imageLayer = dynamic_cast< ImageLayer* > (layer );
-    if (imageLayer)
+    // layer type
+    std::string typeName = typeid(*layer).name();
+    typeName = typeName.substr(typeName.find_last_of(":")+1);
+    LabelControl* typeLabel = new LabelControl(typeName, osg::Vec4(.5,.7,.5,1));
+    grid->setControl( gridCol, gridRow, typeLabel );
+    gridCol++;
+
+    // status indicator
+    LabelControl* statusLabel =
+        layer->getStatus().isError() ? new LabelControl("[error]", osg::Vec4(1,0,0,1)) :
+        !layer->getEnabled()?          new LabelControl("[disabled]", osg::Vec4(1,1,1,0.35)) :
+                                       new LabelControl("[ok]", osg::Vec4(0,1,0,1)) ;
+    grid->setControl( gridCol, gridRow, statusLabel );
+    gridCol++;
+
+    if (visibleLayer && !elevationLayer && visibleLayer->getEnabled())
     {
         // an opacity slider
-        HSliderControl* opacity = new HSliderControl( 0.0f, 1.0f, imageLayer->getOpacity() );
+        HSliderControl* opacity = new HSliderControl( 0.0f, 1.0f, visibleLayer->getOpacity() );
         opacity->setWidth( 125 );
         opacity->setHeight( 12 );
-        opacity->addEventHandler( new LayerOpacityHandler(imageLayer) );
+        opacity->addEventHandler( new LayerOpacityHandler(visibleLayer) );
         grid->setControl( gridCol, gridRow, opacity );
     }
     gridCol++;
 
-    // status indicator
-    LabelControl* statusLabel = layer->getStatus().isOK()
-        ? new LabelControl("[ok]", osg::Vec4(0,1,0,1))
-        : new LabelControl("[error]", osg::Vec4(1,0,0,1));
-    grid->setControl( gridCol, gridRow, statusLabel );
+    // zoom button
+    if (layer->getExtent().isValid())
+    {
+        LabelControl* zoomButton = new LabelControl("GO", 14);
+        zoomButton->setBackColor( .4,.4,.4,1 );
+        zoomButton->setActiveColor( .8,0,0,1 );
+        zoomButton->addEventHandler( new ZoomLayerHandler(layer) );
+        grid->setControl( gridCol, gridRow, zoomButton );
+    }
     gridCol++;
 
     // move buttons
@@ -358,37 +399,33 @@ createLayerItem( Grid* grid, int gridRow, int layerIndex, int numLayers, Terrain
     addRemove->setHorizAlign( Control::ALIGN_CENTER );
     addRemove->setBackColor( .4,.4,.4,1 );
     addRemove->setActiveColor( .8,0,0,1 );
-    if ( isActive )
-        addRemove->addEventHandler( new RemoveLayerHandler(layer) );
-    else
-        addRemove->addEventHandler( new AddLayerHandler(layer) );
+    addRemove->addEventHandler( new RemoveLayerHandler(layer) );
+
     grid->setControl( gridCol, gridRow, addRemove );
     gridCol++;
+
+    if (layer->getStatus().isError())
+    {
+        grid->setControl(gridCol, gridRow, new LabelControl(layer->getStatus().message(), osg::Vec4(1,.2,.2,1)));
+    }
 }
 
 void
-createModelLayerItem( Grid* grid, int gridRow, ModelLayer* layer, bool isActive )
+createInactiveLayerItem( Grid* grid, int gridRow, const std::string& name, const ConfigOptions& lc )
 {
-    // a checkbox to enable/disable the layer:
-    CheckBoxControl* enabled = new CheckBoxControl( layer->getVisible() );
-    enabled->addEventHandler( new ModelLayerVisibleHandler(layer) );
-    grid->setControl( 0, gridRow, enabled );
+    int gridCol = 0;
 
     // the layer name
-    LabelControl* name = new LabelControl( layer->getName() );
-    grid->setControl( 1, gridRow, name );
-
-    LabelControl* statusLabel = layer->getStatus().isOK()
-        ? new LabelControl("[ok]", osg::Vec4(0, 1, 0, 1))
-        : new LabelControl("[error]", osg::Vec4(1, 0, 0, 1));
-    grid->setControl(2, gridRow, statusLabel);
-
-    // an opacity slider
-    HSliderControl* opacity = new HSliderControl( 0.0f, 1.0f, layer->getOpacity() );
-    opacity->setWidth( 125 );
-    opacity->setHeight( 12 );
-    opacity->addEventHandler( new ModelLayerOpacityHandler(layer) );
-    grid->setControl( 3, gridRow, opacity );
+    LabelControl* nameLabel = new LabelControl( name );
+    grid->setControl( gridCol, gridRow, nameLabel );
+    gridCol++;
+    
+    LabelControl* addRemove = new LabelControl( "ADD", 14 );
+    addRemove->setHorizAlign( Control::ALIGN_CENTER );
+    addRemove->setBackColor( .4,.4,.4,1 );
+    addRemove->setActiveColor( .8,0,0,1 );
+    addRemove->addEventHandler( new AddLayerHandler(lc) );
+    grid->setControl( gridCol, gridRow, addRemove );
 }
 
 void
@@ -397,69 +434,39 @@ updateControlPanel()
     // erase all child controls and just rebuild them b/c we're lazy.
 
     //Rebuild all the image layers    
-    s_imageBox->clearControls();
+    s_activeBox->clearControls();
 
     int row = 0;
 
-    LabelControl* activeLabel = new LabelControl( "Image Layers", 20, osg::Vec4f(1,1,0,1) );
-    s_imageBox->setControl( 1, row++, activeLabel );
+    LabelControl* activeLabel = new LabelControl( "Map Layers", 20, osg::Vec4f(1,1,0,1) );
+    s_activeBox->setControl( 1, row++, activeLabel );
 
     // the active map layers:
-    MapFrame mapf( s_activeMap.get(), Map::ENTIRE_MODEL );
-    int layerNum = mapf.imageLayers().size()-1;
-    for( ImageLayerVector::const_reverse_iterator i = mapf.imageLayers().rbegin(); i != mapf.imageLayers().rend(); ++i )
-        createLayerItem( s_imageBox, row++, layerNum--, mapf.imageLayers().size(), i->get(), true );
+    MapFrame mapf( s_activeMap.get() );
 
-    MapFrame mapf2( s_inactiveMap.get() );
-    if ( mapf2.imageLayers().size() > 0 )
+    const LayerVector& layers = mapf.layers();
+    for (int i = layers.size()-1; i >= 0; --i)
     {
-        LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) );
-        s_imageBox->setControl( 1, row++, inactiveLabel );
+        Layer* layer = layers[i].get();
+        addLayerItem(s_activeBox, i, layers.size(), layer, true);
 
-        for( unsigned int i=0; i<mapf2.imageLayers().size(); ++i )
+        if (layer->getStatus().isError())
         {
-            createLayerItem( s_imageBox, row++, -1, -1, mapf2.getImageLayerAt(i), false );
+            OE_WARN << layer->getName() << " : " << layer->getStatus().toString() << "\n";
         }
     }
 
+    // inactive layers:
+    s_inactiveBox->clearControls();
 
-
-
-    //Rebuild the elevation layers
-    s_elevationBox->clearControls();
-
-    row = 0;
-
-    activeLabel = new LabelControl( "Elevation Layers", 20, osg::Vec4f(1,1,0,1) );
-    s_elevationBox->setControl( 1, row++, activeLabel );
-
-    // the active map layers:
-    layerNum = mapf.elevationLayers().size()-1;
-    for( ElevationLayerVector::const_reverse_iterator i = mapf.elevationLayers().rbegin(); i != mapf.elevationLayers().rend(); ++i )
-        createLayerItem( s_elevationBox, row++, layerNum--, mapf.elevationLayers().size(), i->get(), true );
-
-    if ( mapf2.elevationLayers().size() > 0 )
+    if (!_inactive.empty())
     {
-        LabelControl* inactiveLabel = new LabelControl( "Removed:", 18, osg::Vec4f(1,1,0,1) );
-        s_elevationBox->setControl( 1, row++, inactiveLabel );
-
-        for( unsigned int i=0; i<mapf2.elevationLayers().size(); ++i )
+        s_inactiveBox->setControl(0, row++, new LabelControl("Removed:", 18, osg::Vec4f(1,1,0,1)));
+        for (InactiveLayers::const_iterator i = _inactive.begin(); i != _inactive.end(); ++i)
         {
-            createLayerItem( s_elevationBox, row++, -1, -1, mapf2.getElevationLayerAt(i), false );
+            createInactiveLayerItem(s_inactiveBox, row++, i->first, i->second);
         }
     }
 
-
-
-    //Rebuild the model layers
-    s_modelBox->clearControls();
-
-    row = 0;
-
-    activeLabel = new LabelControl( "Model Layers", 20, osg::Vec4f(1,1,0,1) );
-    s_modelBox->setControl( 1, row++, activeLabel );
-
-    // the active map layers:
-    for( ModelLayerVector::const_reverse_iterator i = mapf.modelLayers().rbegin(); i != mapf.modelLayers().rend(); ++i )
-        createModelLayerItem( s_modelBox, row++, i->get(), true );
+    s_inactiveBox->setVisible(!_inactive.empty());
 }
diff --git a/src/applications/osgearth_tracks/osgearth_tracks.cpp b/src/applications/osgearth_tracks/osgearth_tracks.cpp
index 4eb0fb9..c94632f 100644
--- a/src/applications/osgearth_tracks/osgearth_tracks.cpp
+++ b/src/applications/osgearth_tracks/osgearth_tracks.cpp
@@ -169,7 +169,7 @@ void
 createTrackNodes( MapNode* mapNode, osg::Group* parent, const TrackNodeFieldSchema& schema, TrackSims& sims )
 {
     // load an icon to use:
-    osg::ref_ptr<osg::Image> srcImage = osgDB::readImageFile( ICON_URL );
+    osg::ref_ptr<osg::Image> srcImage = osgDB::readRefImageFile( ICON_URL );
     osg::ref_ptr<osg::Image> image;
     ImageUtils::resizeImage( srcImage.get(), ICON_SIZE, ICON_SIZE, image );
 
@@ -184,7 +184,7 @@ createTrackNodes( MapNode* mapNode, osg::Group* parent, const TrackNodeFieldSche
 
         GeoPoint pos(geoSRS, lon0, lat0);
 
-        TrackNode* track = new TrackNode(mapNode, pos, image, schema);
+        TrackNode* track = new TrackNode(mapNode, pos, image.get(), schema);
 
         track->setFieldValue( FIELD_NAME,     Stringify() << "Track:" << i );
         track->setFieldValue( FIELD_POSITION, Stringify() << s_format(pos) );
diff --git a/src/applications/osgearth_transform/osgearth_transform.cpp b/src/applications/osgearth_transform/osgearth_transform.cpp
index 2549d35..b3457d9 100644
--- a/src/applications/osgearth_transform/osgearth_transform.cpp
+++ b/src/applications/osgearth_transform/osgearth_transform.cpp
@@ -64,13 +64,16 @@ struct App
     ui::HSliderControl* uiHeading;
     ui::HSliderControl* uiPitch;
     ui::HSliderControl* uiRoll;
+    ui::CheckBoxControl* uiRelativeZ;
 
     void apply()
     {
+        AltitudeMode altMode = uiRelativeZ->getValue() ? ALTMODE_RELATIVE : ALTMODE_ABSOLUTE;
+
         GeoPoint pos(
             srs,
             uiLon->getValue(), uiLat->getValue(), uiAlt->getValue(),
-            ALTMODE_ABSOLUTE);
+            altMode);
 
         geo->setPosition( pos );
 
@@ -92,9 +95,19 @@ struct Apply : public ui::ControlEventHandler
     App& _app;
 };
 
+struct ZeroAlt : public ui::ControlEventHandler {
+    ZeroAlt(App& app) : _app(app) { }
+    void onClick(ui::Control* control) {
+        _app.uiAlt->setValue(0.0f);
+        _app.apply();
+    }
+    App& _app;
+};
+
 ui::Control* makeUI(App& app)
 {
     ui::Grid* grid = new ui::Grid();
+    grid->setBackColor(0,0,0,0.5);
 
     grid->setControl(0, 0, new ui::LabelControl("Lat:"));
     grid->setControl(0, 1, new ui::LabelControl("Long:"));
@@ -102,15 +115,25 @@ ui::Control* makeUI(App& app)
     grid->setControl(0, 3, new ui::LabelControl("Heading:"));
     grid->setControl(0, 4, new ui::LabelControl("Pitch:"));
     grid->setControl(0, 5, new ui::LabelControl("Roll:"));
-
-    app.uiLat = grid->setControl(1, 0, new ui::HSliderControl(-80.0f, 80.0f, 42.0f, new Apply(app)));
-    app.uiLon = grid->setControl(1, 1, new ui::HSliderControl(-180.0f, 180.0f, 7.0f, new Apply(app)));
-    app.uiAlt = grid->setControl(1, 2, new ui::HSliderControl(50000.0f, 500000.0f, 250000.0f, new Apply(app)));
+    grid->setControl(0, 6, new ui::LabelControl("Relative Z:"));
+
+    app.uiLat = grid->setControl(1, 0, new ui::HSliderControl(40.0f, 50.0f, 44.7433f, new Apply(app)));
+    grid->setControl(2, 0, new LabelControl(app.uiLat));
+    app.uiLon = grid->setControl(1, 1, new ui::HSliderControl(6.0f, 8.0f, 7.0f, new Apply(app)));
+    grid->setControl(2, 1, new LabelControl(app.uiLon));
+    app.uiAlt = grid->setControl(1, 2, new ui::HSliderControl(-3000.0f, 100000.0f, 25000.0f, new Apply(app)));
+    grid->setControl(2, 2, new LabelControl(app.uiAlt));
+    grid->setControl(3, 2, new ButtonControl("Zero", new ZeroAlt(app)));
     app.uiHeading = grid->setControl(1, 3, new ui::HSliderControl(-180.0f, 180.0f, 0.0f, new Apply(app)));
+    grid->setControl(2, 3, new LabelControl(app.uiHeading));
     app.uiPitch   = grid->setControl(1, 4, new ui::HSliderControl(-90.0f, 90.0f, 0.0f, new Apply(app)));
+    grid->setControl(2, 4, new LabelControl(app.uiPitch));
     app.uiRoll    = grid->setControl(1, 5, new ui::HSliderControl(-180.0f, 180.0f, 0.0f, new Apply(app)));
+    grid->setControl(2, 5, new LabelControl(app.uiRoll));
+    app.uiRelativeZ = grid->setControl(1, 6, new ui::CheckBoxControl(true, new Apply(app))); app.uiRelativeZ->setWidth(15.0f);
+    grid->setControl(2, 6, new LabelControl(app.uiRelativeZ));
 
-    app.uiLat->setHorizFill(true, 300.0f);
+    app.uiLat->setHorizFill(true, 700.0f);
     return grid;
 }
 
@@ -136,8 +159,8 @@ main(int argc, char** argv)
 
     // load the model file into the local coordinate frame, which will be
     // +X=east, +Y=north, +Z=up.
-    osg::Node* model = osgDB::readNodeFile("../data/axes.osgt.(1000).scale");
-    if ( !model )
+    osg::ref_ptr<osg::Node> model = osgDB::readRefNodeFile("../data/axes.osgt.(1000).scale.osgearth_shadergen");
+    if (!model.valid())
         return usage(argv[0]);
 
     osg::Group* root = new osg::Group();
@@ -146,12 +169,13 @@ main(int argc, char** argv)
     App app;
     app.srs = mapNode->getMapSRS();
     app.geo = new GeoTransform();
-    app.geo->setTerrain( mapNode->getTerrain() );
     app.pat = new osg::PositionAttitudeTransform();
-    app.pat->addChild( model );
+    app.pat->addChild( model.get() );
     app.geo->addChild( app.pat );
 
-    root->addChild( app.geo );
+    // Place your GeoTransform under the map node and it will automatically support clamping.
+    // If you don't do this, you must call setTerrain to get terrain clamping.
+    mapNode->addChild( app.geo );
     
     viewer.setSceneData( root );
     viewer.getCamera()->setNearFarRatio(0.00002);
diff --git a/src/applications/osgearth_triton/osgearth_triton.cpp b/src/applications/osgearth_triton/osgearth_triton.cpp
index d36e320..8e8fc5a 100644
--- a/src/applications/osgearth_triton/osgearth_triton.cpp
+++ b/src/applications/osgearth_triton/osgearth_triton.cpp
@@ -18,21 +18,22 @@
 */
 #include <osgViewer/Viewer>
 #include <osgDB/FileNameUtils>
+#include <osgEarth/NodeUtils>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
 #include <osgEarthUtil/Controls>
 #include <osgEarthTriton/TritonNode>
 
-#define LC "[osgearth_sundog] "
+#define LC "[osgearth_triton] "
 
 using namespace osgEarth;
 using namespace osgEarth::Util;
 using namespace osgEarth::Triton;
 namespace ui = osgEarth::Util::Controls;
 
+
 struct Settings
 {
-    TritonNode* triton;
     optional<double> chop;
     optional<double> seaState;
     optional<float> alpha;
@@ -51,13 +52,98 @@ struct Settings
             seaState.clear();
         }
 
-        if (alpha.isSet())
+        //if (alpha.isSet())
+        //{
+        //    triton->setAlpha( alpha.get() );
+        //}
+    }
+};
+
+class TritonCallback : public osgEarth::Triton::Callback
+{
+public:
+    TritonCallback(Settings& settings) : _settings(settings) { }
+
+    void onInitialize(Environment& env, Ocean& ocean)
+    {
+        //todo
+    }
+
+    void onDrawOcean(Environment& env, Ocean& ocean)
+    {
+        _settings.apply(env, ocean);
+    }
+
+    Settings& _settings;
+};
+
+
+struct App
+{
+    App()
+    {
+        triton = NULL;
+        mapNode = NULL;        
+    }
+
+    MapNode*    mapNode;
+    TritonNode* triton;
+    Settings    settings;
+
+    osg::Group* getAttachPoint()
+    {
+        return mapNode;
+        //SkyNode* sky = osgEarth::findTopMostNodeOfType<SkyNode>(mapNode);
+        //if (sky)
+        //    return sky;
+        //else
+        //    return mapNode;
+    }
+
+    void addTriton()
+    {
+        // Create TritonNode from TritonOptions
+        osgEarth::Triton::TritonOptions tritonOptions;
+        tritonOptions.user()        = "my_user_name";
+        tritonOptions.licenseCode() = "my_license_code";
+        tritonOptions.maxAltitude() = 10000;
+        tritonOptions.useHeightMap() = true;
+
+        const char* ev_t = ::getenv("TRITON_PATH");
+        if ( ev_t )
         {
-            triton->setAlpha( alpha.get() );
+            tritonOptions.resourcePath() = osgDB::concatPaths(
+                std::string(ev_t),
+                "Resources" );
+
+            OE_INFO << LC 
+                << "Setting resource path to << " << tritonOptions.resourcePath().get()
+                << std::endl;
         }
+        else
+        {
+            OE_WARN << LC
+                << "No resource path! Triton might not initialize properly. "
+                << "Consider setting the TRITON_PATH environment variable."
+                << std::endl;
+        }
+
+
+        triton = new TritonNode(tritonOptions, new TritonCallback(settings));
+        triton->setMapNode(mapNode);
+
+        getAttachPoint()->addChild(triton);
+    }
+
+    void removeTriton()
+    {
+        getAttachPoint()->removeChild(triton);
+        triton = 0L;
     }
 };
-Settings s_settings;
+
+App s_app;
+
 
 
 template<typename T> struct Set : public ui::ControlEventHandler
@@ -67,6 +153,15 @@ template<typename T> struct Set : public ui::ControlEventHandler
     void onValueChanged(ui::Control*, double value) { _var = value; }
 };
 
+struct Toggle : public ui::ControlEventHandler
+{
+    void onValueChanged(ui::Control*, bool value) {
+        if (s_app.triton)
+            s_app.removeTriton();
+        else
+            s_app.addTriton();
+    }
+};
 
 Container* createUI()
 {
@@ -75,33 +170,22 @@ Container* createUI()
     Grid* grid = box->addControl(new Grid());
     int r=0;
     grid->setControl(0, r, new LabelControl("Chop"));
-    grid->setControl(1, r, new HSliderControl(0, 3, 0, new Set<double>(s_settings.chop)));
+    grid->setControl(1, r, new HSliderControl(0, 3, 0, new Set<double>(s_app.settings.chop)));
     ++r;
     grid->setControl(0, r, new LabelControl("Sea State"));
-    grid->setControl(1, r, new HSliderControl(0, 12, 5, new Set<double>(s_settings.seaState)));
+    grid->setControl(1, r, new HSliderControl(0, 12, 5, new Set<double>(s_app.settings.seaState)));
     ++r;  
     grid->setControl(0, r, new LabelControl("Alpha"));
-    grid->setControl(1, r, new HSliderControl(0, 1.0, 1.0, new Set<float>(s_settings.alpha)));
+    grid->setControl(1, r, new HSliderControl(0, 1.0, 1.0, new Set<float>(s_app.settings.alpha)));
     ++r;
+    grid->setControl(0, r, new LabelControl("Toggle"));
+    grid->setControl(1, r, new CheckBoxControl(false, new Toggle()));
+
     grid->getControl(1, r-1)->setHorizFill(true,200);
 
     return box;
 }
 
-class TritonCallback : public osgEarth::Triton::Callback
-{
-public:
-    void onInitialize(Environment& env, Ocean& ocean)
-    {
-        //todo
-    }
-
-    void onDrawOcean(Environment& env, Ocean& ocean)
-    {
-        s_settings.apply(env, ocean);
-    }
-};
-
 
 int
 usage(const char* name)
@@ -141,44 +225,8 @@ main(int argc, char** argv)
 
         viewer.setSceneData( node );
 
-        MapNode* mapNode = MapNode::findMapNode( node );
-
-        // Create TritonNode from TritonOptions
-        osgEarth::Triton::TritonOptions tritonOptions;
-        tritonOptions.user()        = "my_user_name";
-        tritonOptions.licenseCode() = "my_license_code";
-        tritonOptions.maxAltitude() = 10000;
-
-        const char* ev_t = ::getenv("TRITON_PATH");
-        if ( ev_t )
-        {
-            tritonOptions.resourcePath() = osgDB::concatPaths(
-                std::string(ev_t),
-                "Resources" );
-
-            OE_INFO << LC 
-                << "Setting resource path to << " << tritonOptions.resourcePath().get()
-                << std::endl;
-        }
-        else
-        {
-            OE_WARN << LC
-                << "No resource path! Triton might not initialize properly. "
-                << "Consider setting the TRITON_PATH environment variable."
-                << std::endl;
-        }
-
-        s_settings.triton = new TritonNode(
-            mapNode,
-            tritonOptions,
-            new TritonCallback() );
-
-        // Insert under a sky if there is one.
-        SkyNode* sky = osgEarth::findTopMostNodeOfType<SkyNode>(node);
-        if (sky)
-            sky->addChild( s_settings.triton );
-        else
-            node->addChild( s_settings.triton );
+        s_app.mapNode = MapNode::get( node );
+        //s_app.addTriton();
 
         return viewer.run();
     }
diff --git a/src/applications/osgearth_clamp/CMakeLists.txt b/src/applications/osgearth_video/CMakeLists.txt
similarity index 71%
rename from src/applications/osgearth_clamp/CMakeLists.txt
rename to src/applications/osgearth_video/CMakeLists.txt
index 8bf1af5..f4e7c80 100644
--- a/src/applications/osgearth_clamp/CMakeLists.txt
+++ b/src/applications/osgearth_video/CMakeLists.txt
@@ -1,7 +1,7 @@
 INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
 SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGVIEWER_LIBRARY OPENTHREADS_LIBRARY)
 
-SET(TARGET_SRC osgearth_clamp.cpp )
+SET(TARGET_SRC osgearth_video.cpp )
 
 #### end var setup  ###
-SETUP_APPLICATION(osgearth_clamp)
\ No newline at end of file
+SETUP_APPLICATION(osgearth_video)
\ No newline at end of file
diff --git a/src/applications/osgearth_map/osgearth_map.cpp b/src/applications/osgearth_video/osgearth_video.cpp
similarity index 73%
copy from src/applications/osgearth_map/osgearth_map.cpp
copy to src/applications/osgearth_video/osgearth_video.cpp
index 5e7a833..70f1bc8 100644
--- a/src/applications/osgearth_map/osgearth_map.cpp
+++ b/src/applications/osgearth_video/osgearth_video.cpp
@@ -26,11 +26,17 @@
 #include <osgViewer/Viewer>
 #include <osgViewer/ViewerEventHandlers>
 #include <osgEarth/MapNode>
+#include <osgEarth/Registry>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/VideoLayer>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/AutoClipPlaneHandler>
 #include <osgEarthUtil/Controls>
 #include <osgEarthSymbology/Color>
 #include <osgEarthDrivers/tms/TMSOptions>
+#include <osgEarthDrivers/wms/WMSOptions>
+#include <osgEarthDrivers/gdal/GDALOptions>
+#include <osg/ImageStream>
 
 using namespace osgEarth;
 using namespace osgEarth::Drivers;
@@ -44,18 +50,32 @@ main(int argc, char** argv)
 {
     osg::ArgumentParser arguments(&argc,argv);
 
-    // create the map.
+    // create the empty map.
     Map* map = new Map();
 
-    // add a TMS imager layer:
+    // add a TMS imagery layer:
     TMSOptions imagery;
-    imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/7/";
-    map->addImageLayer( new ImageLayer("Imagery", imagery) );
+    imagery.url() = "http://readymap.org/readymap/tiles/1.0.0/22/";
+    map->addLayer( new ImageLayer("ReadyMap Imagery", imagery) );
 
     // add a TMS elevation layer:
     TMSOptions elevation;
-    elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/9/";
-    map->addElevationLayer( new ElevationLayer("Elevation", elevation) );
+    elevation.url() = "http://readymap.org/readymap/tiles/1.0.0/116/";
+    map->addLayer( new ElevationLayer("ReadyMap Elevation", elevation) );
+   
+    // Load command line arguments as videos.
+    for(int pos=1;pos<arguments.argc();++pos)
+    {
+        if (!arguments.isOption(pos))
+        {
+            std::string filename = arguments[ pos ];
+            OE_NOTICE << "Loading " << filename << std::endl;
+            VideoLayerOptions opt;
+            opt.url() = filename;
+            VideoLayer* layer = new VideoLayer(opt);
+            map->addLayer(layer);
+        }
+    }
 
     // make the map scene graph:
     MapNode* node = new MapNode( map );
@@ -74,4 +94,4 @@ main(int argc, char** argv)
     viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
 
     return viewer.run();
-}
+}
\ No newline at end of file
diff --git a/src/applications/osgearth_viewer/osgearth_viewer.cpp b/src/applications/osgearth_viewer/osgearth_viewer.cpp
index 2e4b118..4e9f57c 100644
--- a/src/applications/osgearth_viewer/osgearth_viewer.cpp
+++ b/src/applications/osgearth_viewer/osgearth_viewer.cpp
@@ -24,9 +24,10 @@
 #include <osgEarth/Notify>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
-
-#include <osgEarth/Cache>
-#include <osgEarthDrivers/cache_filesystem/FileSystemCache>
+#include <osgEarth/MapNode>
+#include <osgEarth/ThreadingUtils>
+#include <osgEarth/Metrics>
+#include <iostream>
 
 #define LC "[viewer] "
 
@@ -40,9 +41,11 @@ usage(const char* name)
         << "\nUsage: " << name << " file.earth" << std::endl
         << MapNodeHelper().usage() << std::endl;
 
+    getchar();
     return 0;
 }
 
+
 int
 main(int argc, char** argv)
 {
@@ -55,6 +58,8 @@ main(int argc, char** argv)
     float vfov = -1.0f;
     arguments.read("--vfov", vfov);
 
+    
+
     // create a viewer:
     osgViewer::Viewer viewer(arguments);
 
@@ -88,10 +93,12 @@ main(int argc, char** argv)
     if ( node )
     {
         viewer.setSceneData( node );
-        viewer.run();
+        Metrics::run(viewer);
     }
     else
     {
         return usage(argv[0]);
     }
+
+    return 0;
 }
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/AppDelegate.h b/src/applications/osgearth_viewerIOS/AppDelegate.h
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/AppDelegate.h
rename to src/applications/osgearth_viewerIOS/AppDelegate.h
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/AppDelegate.m b/src/applications/osgearth_viewerIOS/AppDelegate.mm
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/AppDelegate.m
rename to src/applications/osgearth_viewerIOS/AppDelegate.mm
diff --git a/src/applications/osgearth_viewerIOS/CMakeLists.txt b/src/applications/osgearth_viewerIOS/CMakeLists.txt
new file mode 100644
index 0000000..6a9780a
--- /dev/null
+++ b/src/applications/osgearth_viewerIOS/CMakeLists.txt
@@ -0,0 +1,143 @@
+MACRO(LINK_OSG_STATIC_PLUGINS)
+    FOREACH(LINKLIB ${OSG_STATIC_PLUGINS})
+        SET(OSG_PLUGINS_PATH "${OSG_DIR}/lib")
+        TARGET_LINK_LIBRARIES(${TARGET_TARGETNAME} optimized "${OSG_PLUGINS_PATH}/lib${LINKLIB}.a" debug "${OSG_PLUGINS_PATH}/lib${LINKLIB}${CMAKE_DEBUG_POSTFIX}.a")
+    ENDFOREACH(LINKLIB)
+ENDMACRO(LINK_OSG_STATIC_PLUGINS)
+
+INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
+
+SET(TARGET_LIBRARIES_VARS OPENTHREADS_LIBRARY OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGGA_LIBRARY OSGVIEWER_LIBRARY
+                          OSGANIMATION_LIBRARY OSGFX_LIBRARY OSGPARTICLE_LIBRARY OSGSHADOW_LIBRARY OSGSIM_LIBRARY OSGTERRAIN_LIBRARY OSGTEXT_LIBRARY
+                          CURL_LIBRARY FREETYPE_LIBRARY GDAL_LIBRARY PROJ_LIBRARY GEOS_LIBRARY SQLITE3_LIBRARY)
+
+SET(TARGET_COMMON_LIBRARIES ${TARGET_COMMON_LIBRARIES} osgEarthUtil)
+
+SET(TARGET_ADDED_LIBRARIES osgdb_osgearth_agglite
+osgdb_osgearth_arcgis
+osgdb_osgearth_arcgis_map_cache
+osgdb_osgearth_bing
+osgdb_osgearth_bumpmap
+osgdb_osgearth_cache_filesystem
+osgdb_osgearth_colorramp
+osgdb_osgearth_debug
+osgdb_osgearth_detail
+osgdb_earth
+osgdb_osgearth_engine_byo
+osgdb_osgearth_engine_mp
+osgdb_osgearth_engine_rex
+osgdb_osgearth_feature_elevation
+osgdb_osgearth_feature_ogr
+osgdb_osgearth_feature_raster
+osgdb_osgearth_feature_tfs
+osgdb_osgearth_feature_wfs
+osgdb_osgearth_feature_xyz
+osgdb_osgearth_featurefilter_intersect
+osgdb_osgearth_featurefilter_join
+osgdb_osgearth_gdal
+osgdb_kml
+osgdb_osgearth_label_annotation
+osgdb_osgearth_mapinspector
+osgdb_osgearth_mask_feature
+osgdb_osgearth_mbtiles
+osgdb_osgearth_model_feature_geom
+osgdb_osgearth_model_simple
+osgdb_osgearth_monitor
+osgdb_osgearth_noise
+osgdb_osgearth_ocean_simple
+osgdb_osgearth_osg
+osgdb_osgearth_quadkey
+osgdb_osgearth_refresh
+osgdb_osgearth_scriptengine_javascript
+osgdb_osgearth_sky_gl
+osgdb_osgearth_sky_simple
+osgdb_osgearth_skyview
+osgdb_osgearth_splat_mask
+osgdb_template
+osgdb_osgearth_template_matclass
+osgdb_osgearth_terrainshader
+osgdb_osgearth_tilecache
+osgdb_osgearth_tileindex
+osgdb_osgearth_tileservice
+osgdb_osgearth_tms
+osgdb_osgearth_vdatum_egm84
+osgdb_osgearth_vdatum_egm96
+osgdb_osgearth_vdatum_egm2008
+osgdb_osgearth_viewpoints
+osgdb_osgearth_vpb
+osgdb_osgearth_wcs
+osgdb_osgearth_wms
+osgdb_osgearth_xyz
+osgdb_osgearth_yahoo)
+
+SET(OSG_STATIC_PLUGINS osgdb_tiff osgdb_rgb osgdb_imageio osgdb_curl osgdb_zip osgdb_freetype osgdb_osg osgdb_ive osgdb_obj osgdb_shp osgdb_openflight osgdb_rot osgdb_scale osgdb_trans)
+
+SET(OSG_STATIC_PLUGINS ${OSG_STATIC_PLUGINS}
+    osgdb_deprecated_osg osgdb_deprecated_osganimation osgdb_deprecated_osgfx
+    osgdb_deprecated_osgparticle osgdb_deprecated_osgshadow osgdb_deprecated_osgsim
+    osgdb_deprecated_osgterrain osgdb_deprecated_osgtext osgdb_deprecated_osgviewer
+    osgdb_deprecated_osgvolume
+)
+
+
+SET(OSG_STATIC_PLUGINS ${OSG_STATIC_PLUGINS}
+    osgdb_serializers_osg osgdb_serializers_osganimation osgdb_serializers_osgfx
+    osgdb_serializers_osgmanipulator osgdb_serializers_osgparticle
+    osgdb_serializers_osgshadow osgdb_serializers_osgsim osgdb_serializers_osgterrain
+    osgdb_serializers_osgtext osgdb_serializers_osgutil osgdb_serializers_osgviewer
+    osgdb_serializers_osgvolume
+)
+
+SET(RESOURCE_FILES  
+    StartViewerController.xib
+    ViewController.xib
+    ${CMAKE_SOURCE_DIR}/tests/readymap.earth
+)
+
+SET(TARGET_SRC
+    AppDelegate.h
+    AppDelegate.mm
+    ViewController.h
+    ViewController.mm
+    StartViewerController.h
+    StartViewerController.mm
+    main.mm
+    osgPlugins.h
+    osgEarthViewerIOS-Info.plist
+    ${RESOURCE_FILES}
+)
+
+
+#backup setting
+SET(TMP_OSGEARTH_BUILD_APPLICATION_BUNDLES {$OSGEARTH_BUILD_APPLICATION_BUNDLES}) # make sure its an app bundle
+SET(OSGEARTH_BUILD_APPLICATION_BUNDLES TRUE)
+
+SET(TMP_VPB_BUILD_APPLICATION_BUNDLES {$VPB_BUILD_APPLICATION_BUNDLES}) # make sure its an app bundle
+SET(VPB_BUILD_APPLICATION_BUNDLES TRUE)
+
+SET(TMP_CMAKE_OSX_ARCHITECTURES {$CMAKE_OSX_ARCHITECTURES}) # for now exclude armv7s as freetypes is missing it
+SET(CMAKE_OSX_ARCHITECTURES "armv7;arm64")
+
+SET(MACOSX_DEPLOYMENT_TARGET, ${IPHONE_VERSION_MIN})
+
+SETUP_EXAMPLE(osgearth_viewer_ios)
+LINK_OSG_STATIC_PLUGINS()
+
+SET_TARGET_PROPERTIES(${TARGET_TARGETNAME} PROPERTIES XCODE_ATTRIBUTE_PRODUCT_TYPE "com.apple.product-type.application"
+                                                                 XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer"
+                                                                 XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${IPHONE_VERSION_MIN}
+                                                                 XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
+                                                                 XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+SET(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT "dwarf")
+SET_SOURCE_FILES_PROPERTIES(${RESOURCE_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+
+
+SET(CMAKE_EXE_LINKER_FLAGS "-framework QuartzCore -framework Foundation -framework UIKit 
+-framework ImageIO -framework CoreImage -framework Accelerate -framework MobileCoreServices -framework CoreGraphics 
+-framework JavaScriptCore -framework Security -lsqlite3 -licucore -lz -liconv -lbz2")
+
+#restore setting
+SET(OSGEARTH_BUILD_APPLICATION_BUNDLES {$TMP_OSGEARTH_BUILD_APPLICATION_BUNDLES})
+SET(VPB_BUILD_APPLICATION_BUNDLES {$TMP_VPB_BUILD_APPLICATION_BUNDLES})
+
+SET(CMAKE_OSX_ARCHITECTURES {$TMP_CMAKE_OSX_ARCHITECTURES})
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/MultiTouchManipulator/EarthMultiTouchManipulator.cpp b/src/applications/osgearth_viewerIOS/EarthMultiTouchManipulator.cpp
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/MultiTouchManipulator/EarthMultiTouchManipulator.cpp
rename to src/applications/osgearth_viewerIOS/EarthMultiTouchManipulator.cpp
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/MultiTouchManipulator/EarthMultiTouchManipulator.h b/src/applications/osgearth_viewerIOS/EarthMultiTouchManipulator.h
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/MultiTouchManipulator/EarthMultiTouchManipulator.h
rename to src/applications/osgearth_viewerIOS/EarthMultiTouchManipulator.h
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.h b/src/applications/osgearth_viewerIOS/StartViewerController.h
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.h
rename to src/applications/osgearth_viewerIOS/StartViewerController.h
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.m b/src/applications/osgearth_viewerIOS/StartViewerController.mm
similarity index 80%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.m
rename to src/applications/osgearth_viewerIOS/StartViewerController.mm
index 517121c..a133bdb 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.m
+++ b/src/applications/osgearth_viewerIOS/StartViewerController.mm
@@ -11,6 +11,8 @@
 #include <osgDB/FileUtils>
 #include <osgDB/FileNameUtils>
 
+static NSString* s_autoloadFile = @""; //readymap_flat.earth";
+
 @interface StartViewerController ()
 
 @end
@@ -43,7 +45,8 @@
     fileArray = [[NSMutableArray alloc] init];
     
     std::string fullPath = osgDB::findDataFile("tests/readymap.earth");
-    
+    if(fullPath.empty()) fullPath = osgDB::findDataFile("readymap.earth");
+
     osgDB::DirectoryContents dirContents = osgDB::getDirectoryContents(osgDB::getFilePath(fullPath));
     for(unsigned int i=0; i<dirContents.size(); i++){
         //OSG_ALWAYS << "Dir item: " << dirContents[i] << std::endl;
@@ -52,9 +55,14 @@
             [fileArray addObject:nsFile];
         }
     }
-     
-    currentSelection = [fileArray count]-1;
-    [pickerView selectRow:currentSelection inComponent:0 animated:NO];  
+    
+    if([fileArray count] > 0) {
+        currentSelection = [fileArray count]-1;
+        [pickerView selectRow:currentSelection inComponent:0 animated:NO];
+    }
+
+    if(s_autoloadFile != nil && [s_autoloadFile length] > 0)
+        [self loadEarthView:s_autoloadFile];
 }
 
 - (void)viewDidUnload
@@ -72,12 +80,17 @@
 
 -(IBAction)onStartViewer
 {
-    /*if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
-     self.osgEarthViewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil] autorelease];
-     } else {
-     self.osgEarthViewController = [[[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil] autorelease];
-     }*/
-    self.osgEarthViewController = [[ViewController alloc] intWithFileName:[fileArray objectAtIndex:currentSelection]];
+    [self loadEarthView:[fileArray objectAtIndex:currentSelection]];
+}
+
+-(void) loadEarthView:(NSString*)aFile
+{
+    if(self.osgEarthViewController != nil) {
+        [self.osgEarthViewController release];
+        self.osgEarthViewController = nil;
+    }
+    
+    self.osgEarthViewController = [[ViewController alloc] intWithFileName:aFile];
     [self.osgEarthViewController startAnimation]; 
     [self.view addSubview:self.osgEarthViewController.view];
 }
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.xib b/src/applications/osgearth_viewerIOS/StartViewerController.xib
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/StartViewerController.xib
rename to src/applications/osgearth_viewerIOS/StartViewerController.xib
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.h b/src/applications/osgearth_viewerIOS/ViewController.h
similarity index 92%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.h
rename to src/applications/osgearth_viewerIOS/ViewController.h
index 1f5e80f..6455c65 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.h
+++ b/src/applications/osgearth_viewerIOS/ViewController.h
@@ -25,4 +25,6 @@
 - (void)startAnimation;
 - (void)stopAnimation;
 
+- (IBAction)onBackClicked:(id)sender;
+
 @end
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m b/src/applications/osgearth_viewerIOS/ViewController.mm
similarity index 65%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
rename to src/applications/osgearth_viewerIOS/ViewController.mm
index 80d7630..c09bc69 100644
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ViewController.m
+++ b/src/applications/osgearth_viewerIOS/ViewController.mm
@@ -9,13 +9,14 @@
 
 #include "osgPlugins.h"
 
+#include <osg/Material>
 #include <osgDB/FileUtils>
 #include <osgDB/FileNameUtils>
 
 #include <osgViewer/api/IOS/GraphicsWindowIOS>
 
 #include <osgEarth/Viewpoint>
-#include <osgEarthUtil/SkyNode>
+#include <osgEarthUtil/Sky>
 #include <osgEarthUtil/EarthManipulator>
 #include <osgEarthUtil/ExampleResources>
 
@@ -34,7 +35,8 @@ using namespace osgEarth::Util;
 
 - (id)intWithFileName:(NSString*)file
 {
-    self = [super init];
+    //self = [super init];
+    self = [super initWithNibName:@"ViewController" bundle:nil];
     if(self){
         
         _file = [file cStringUsingEncoding:NSASCIIStringEncoding];
@@ -53,18 +55,6 @@ using namespace osgEarth::Util;
 }
 
 - (void)loadOsgEarthDemoScene{
-
-    // install our default manipulator (do this before calling load)
-    //    _viewer->setCameraManipulator( new osgEarth::Util::EarthManipulator() );
-    
-    // This chunk inverts the Y axis.
-    osgEarth::Util::EarthManipulator* manip = new osgEarth::Util::EarthManipulator();
-    //osgEarth::Util::EarthManipulator::ActionOptions options;
-    //options.add(osgEarth::Util::EarthManipulator::OPTION_SCALE_Y, -1.0);
-    //manip->getSettings()->bindMouse(osgEarth::Util::EarthManipulator::ACTION_EARTH_DRAG, osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON, 0L, options);
-    manip->getSettings()->setThrowingEnabled(true);
-    manip->getSettings()->setThrowDecayRate(0.1);
-    _viewer->setCameraManipulator( manip );
     
     osg::Node* node = osgDB::readNodeFile(osgDB::findDataFile("tests/" + _file));
     if ( !node )
@@ -80,34 +70,10 @@ using namespace osgEarth::Util;
         return;
     }
     
-    // warn about not having an earth manip
-    osgEarth::Util::EarthManipulator* manip_temp = dynamic_cast<osgEarth::Util::EarthManipulator*>(_viewer->getCameraManipulator());
-    if ( manip_temp == 0L )
-    {
-        OSG_WARN << "Helper used before installing an EarthManipulator" << std::endl;
-    }
-    
     // a root node to hold everything:
     osg::Group* root = new osg::Group();
     root->addChild( mapNode.get() );
 
-    
-    //have to add these
-    osg::Material* material = new osg::Material();
-    material->setAmbient(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
-    material->setDiffuse(osg::Material::FRONT, osg::Vec4(0.9,0.9,0.9,1.0));
-    material->setSpecular(osg::Material::FRONT, osg::Vec4(0.4,0.4,0.4,1.0));
-    root->getOrCreateStateSet()->setAttribute(material); //lighting doesn't work without a material for some reason
-    root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);//comment out to disable lighting
-    
-    double hours = 12.0f;
-    float ambientBrightness = 1.0f;
-    osgEarth::Util::SkyNode* sky = new osgEarth::Util::SkyNode( mapNode->getMap() );
-    sky->setAmbientBrightness( ambientBrightness );
-    sky->setDateTime( 1984, 11, 8, hours );
-    sky->attach( _viewer, 0 );
-    root->addChild( sky );
-    
     _viewer->setSceneData( root );
 }
 
@@ -120,9 +86,19 @@ using namespace osgEarth::Util;
     
     setenv("GDAL_DATA", dataPath.c_str(), 1);
     
-    osg::setNotifyLevel(osg::DEBUG_FP);
+    osg::setNotifyLevel(osg::FATAL);
     osgEarth::setNotifyLevel(osg::DEBUG_FP);
     
+    // thread-safe initialization of the OSG wrapper manager. Calling this here
+    // prevents the "unsupported wrapper" messages from OSG
+    osgDB::Registry::instance()->getObjectWrapperManager()->findWrapper("osg::Image");
+    
+    // ensure tiff plugin is used over imageio
+    osgDB::Registry::instance()->addFileExtensionAlias("tiff", "tiff");
+    osgDB::Registry::instance()->addFileExtensionAlias("tif", "tiff");
+    
+    osg::DisplaySettings::instance()->setVertexBufferHint(osg::DisplaySettings::VertexBufferHint::VERTEX_BUFFER_OBJECT);
+    
     //get screen scale
     UIScreen* screen = [UIScreen mainScreen];
     float scale = 1.0f;
@@ -138,6 +114,8 @@ using namespace osgEarth::Util;
     
     //create the viewer
 	_viewer = new osgViewer::Viewer();
+    // just do single threaded
+    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
     
 	// Setup the traits parameters
 	traits->x = 0;
@@ -148,7 +126,7 @@ using namespace osgEarth::Util;
 	traits->alpha = 8;
     //traits->samples = 4;
     //traits->sampleBuffers = 2;
-	traits->stencil = 0;
+	traits->stencil = 8;
 	traits->windowDecoration = false;
 	traits->doubleBuffer = true;
 	traits->sharedContext = 0;
@@ -163,6 +141,9 @@ using namespace osgEarth::Util;
 	if(graphicsContext)
 	{
         _viewer->getCamera()->setGraphicsContext(graphicsContext);
+        _viewer->realize();
+        osgViewer::GraphicsWindowIOS* osgWindow = dynamic_cast<osgViewer::GraphicsWindowIOS*>(_viewer->getCamera()->getGraphicsContext());
+        if(osgWindow) [self.view sendSubviewToBack:(UIView*)osgWindow->getView()];
     }
     
     _viewer->getCamera()->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
@@ -173,23 +154,26 @@ using namespace osgEarth::Util;
                                                            0.1f, 10000.0f);
 
 
+    // Tell the database pager to not modify the unref settings
+    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy( true, false );
+
+
+    // install our default manipulator
+    osgEarth::Util::EarthManipulator* manip = new osgEarth::Util::EarthManipulator();
+    //manip->getSettings()->setThrowingEnabled(true);
+    //manip->getSettings()->setThrowDecayRate(0.1);
+    _viewer->setCameraManipulator( manip );
+
+    // disable the small-feature culling
+    _viewer->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
+
+    // set a near/far ratio that is smaller than the default. This allows us to get
+    // closer to the ground without near clipping. If you need more, use --logdepth
+    _viewer->getCamera()->setNearFarRatio(0.0001);
+
     //load
     [self loadOsgEarthDemoScene];
-    
-    // configure the near/far so we don't clip things that are up close
-    _viewer->getCamera()->setNearFarRatio(0.00002);
-    
-    //optimize viewer and db pager
-    _viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
-    _viewer->getCamera()->setLODScale(_viewer->getCamera()->getLODScale()/2.0);
-    
-    // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of
-    // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks.
-//    _viewer->getDatabasePager()->setDoPreCompile( true );
-//   _viewer->getDatabasePager()->setTargetMaximumNumberOfPageLOD(0);
-//    _viewer->getDatabasePager()->setUnrefImageDataAfterApplyPolicy(true,true);
 
-  
     _isAnimating=false;
     [self startAnimation];
     
@@ -239,11 +223,18 @@ using namespace osgEarth::Util;
     }
 }
 
+- (IBAction)onBackClicked:(id)sender {
+    [self stopAnimation];
+    _viewer->done();
+    //_viewer->stopThreading();
+    //_viewer->getDatabasePager()->cancel();
+    [self.view removeFromSuperview];
+}
+
 
 - (void)update:(CADisplayLink *)sender
 {
-    //
-    _viewer->frame();
+    if(_viewer != NULL && _isAnimating) _viewer->frame();
 }
 
 
diff --git a/src/applications/osgearth_viewerIOS/ViewController.xib b/src/applications/osgearth_viewerIOS/ViewController.xib
new file mode 100644
index 0000000..0fb382c
--- /dev/null
+++ b/src/applications/osgearth_viewerIOS/ViewController.xib
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" colorMatched="YES">
+    <device id="ipad9_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ViewController">
+            <connections>
+                <outlet property="view" destination="1" id="3"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="1">
+            <rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="asM-JQ-Lh3">
+                    <rect key="frame" x="26" y="20" width="34" height="30"/>
+                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                    <state key="normal" title="Back"/>
+                    <connections>
+                        <action selector="onBackClicked:" destination="-1" eventType="touchUpInside" id="ftb-OI-onZ"/>
+                    </connections>
+                </button>
+            </subviews>
+            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </view>
+    </objects>
+    <simulatedMetricsContainer key="defaultSimulatedMetrics">
+        <simulatedStatusBarMetrics key="statusBar"/>
+        <simulatedOrientationMetrics key="orientation"/>
+        <simulatedScreenMetrics key="destination"/>
+    </simulatedMetricsContainer>
+</document>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/main.m b/src/applications/osgearth_viewerIOS/main.mm
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/main.m
rename to src/applications/osgearth_viewerIOS/main.mm
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgEarthViewerIOS-Info.plist b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS-Info.plist
similarity index 100%
rename from src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgEarthViewerIOS-Info.plist
rename to src/applications/osgearth_viewerIOS/osgEarthViewerIOS-Info.plist
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
deleted file mode 100644
index d95596c..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.pbxproj
+++ /dev/null
@@ -1,1066 +0,0 @@
-// !$*UTF8*$!
-{
-	archiveVersion = 1;
-	classes = {
-	};
-	objectVersion = 46;
-	objects = {
-
-/* Begin PBXBuildFile section */
-		1F823FFE173C1D20003B519D /* libosgdb_osgearth_engine_mp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F823FFD173C1D20003B519D /* libosgdb_osgearth_engine_mp.a */; };
-		1F8566A817666C29005BCD2B /* libosgdb_osgearth_scriptengine_javascriptcore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F8566A717666C29005BCD2B /* libosgdb_osgearth_scriptengine_javascriptcore.a */; };
-		1F8566AA17666E67005BCD2B /* libJavaScriptCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F8566A917666E67005BCD2B /* libJavaScriptCore.a */; };
-		1F8566AC17666EC1005BCD2B /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F8566AB17666EC1005BCD2B /* libicucore.dylib */; };
-		1FA5C122177B608000A161FF /* gdal_data in Resources */ = {isa = PBXBuildFile; fileRef = 1FA5C121177B608000A161FF /* gdal_data */; };
-		90283E5615C6FB3E00620EEF /* tests in Resources */ = {isa = PBXBuildFile; fileRef = 90283E5415C6FB3E00620EEF /* tests */; };
-		90283E5715C6FB3E00620EEF /* data in Resources */ = {isa = PBXBuildFile; fileRef = 90283E5515C6FB3E00620EEF /* data */; };
-		90283E5D15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */; };
-		903B45DB15C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */; };
-		9048FB2415FA9DE50012C900 /* libosgdb_tiff.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */; };
-		904E22771691CC42002D66FD /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 904E22751691CC42002D66FD /* Accelerate.framework */; };
-		904E22781691CC42002D66FD /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 904E22761691CC42002D66FD /* MobileCoreServices.framework */; };
-		9051000115B1EDFD00D9ABD3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000015B1EDFD00D9ABD3 /* Foundation.framework */; };
-		9051000315B1EDFD00D9ABD3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */; };
-		9051000715B1EDFD00D9ABD3 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */; };
-		9051000D15B1EDFD00D9ABD3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */; };
-		9051000F15B1EDFD00D9ABD3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051000E15B1EDFD00D9ABD3 /* main.m */; };
-		9051001315B1EDFD00D9ABD3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051001215B1EDFD00D9ABD3 /* AppDelegate.m */; };
-		9051001A15B1EDFD00D9ABD3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9051001915B1EDFD00D9ABD3 /* ViewController.m */; };
-		9051001D15B1EDFD00D9ABD3 /* ViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */; };
-		9051002015B1EDFD00D9ABD3 /* ViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */; };
-		905100C315B20AC600D9ABD3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100C215B20AC600D9ABD3 /* ImageIO.framework */; };
-		905100C515B20AD100D9ABD3 /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100C415B20AD100D9ABD3 /* CoreImage.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-		905100CB15B2101E00D9ABD3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CA15B2101E00D9ABD3 /* QuartzCore.framework */; };
-		905100CD15B217A800D9ABD3 /* libc++.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CC15B217A800D9ABD3 /* libc++.dylib */; };
-		905100CF15B217B500D9ABD3 /* libz.1.1.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */; };
-		905100D115B2185000D9ABD3 /* libiconv.2.4.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */; };
-		9051FFFF15B1EDFD00D9ABD3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */; };
-		907D033915B86F8700575110 /* libOpenThreads.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DC15B86F8700575110 /* libOpenThreads.a */; };
-		907D033A15B86F8700575110 /* libosg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DD15B86F8700575110 /* libosg.a */; };
-		907D033B15B86F8700575110 /* libosgAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DE15B86F8700575110 /* libosgAnimation.a */; };
-		907D033C15B86F8700575110 /* libosgdb_3dc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02DF15B86F8700575110 /* libosgdb_3dc.a */; };
-		907D033D15B86F8700575110 /* libosgdb_3ds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E015B86F8700575110 /* libosgdb_3ds.a */; };
-		907D033E15B86F8700575110 /* libosgdb_ac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E115B86F8700575110 /* libosgdb_ac.a */; };
-		907D033F15B86F8700575110 /* libosgdb_bmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E215B86F8700575110 /* libosgdb_bmp.a */; };
-		907D034015B86F8700575110 /* libosgdb_bsp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E315B86F8700575110 /* libosgdb_bsp.a */; };
-		907D034115B86F8700575110 /* libosgdb_bvh.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E415B86F8700575110 /* libosgdb_bvh.a */; };
-		907D034215B86F8700575110 /* libosgdb_cfg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E515B86F8700575110 /* libosgdb_cfg.a */; };
-		907D034315B86F8700575110 /* libosgdb_curl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E615B86F8700575110 /* libosgdb_curl.a */; };
-		907D034415B86F8700575110 /* libosgdb_dds.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E715B86F8700575110 /* libosgdb_dds.a */; };
-		907D034515B86F8700575110 /* libosgdb_deprecated_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */; };
-		907D034615B86F8700575110 /* libosgdb_deprecated_osganimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */; };
-		907D034715B86F8700575110 /* libosgdb_deprecated_osgfx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */; };
-		907D034815B86F8700575110 /* libosgdb_deprecated_osgparticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */; };
-		907D034915B86F8700575110 /* libosgdb_deprecated_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */; };
-		907D034A15B86F8700575110 /* libosgdb_deprecated_osgsim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */; };
-		907D034B15B86F8700575110 /* libosgdb_deprecated_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */; };
-		907D034C15B86F8700575110 /* libosgdb_deprecated_osgtext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */; };
-		907D034D15B86F8700575110 /* libosgdb_deprecated_osgviewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */; };
-		907D034E15B86F8700575110 /* libosgdb_deprecated_osgvolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */; };
-		907D034F15B86F8700575110 /* libosgdb_deprecated_osgwidget.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */; };
-		907D035015B86F8700575110 /* libosgdb_dot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F315B86F8700575110 /* libosgdb_dot.a */; };
-		907D035115B86F8700575110 /* libosgdb_dw.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F415B86F8700575110 /* libosgdb_dw.a */; };
-		907D035215B86F8700575110 /* libosgdb_dxf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F515B86F8700575110 /* libosgdb_dxf.a */; };
-		907D035315B86F8700575110 /* libosgdb_freetype.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F615B86F8700575110 /* libosgdb_freetype.a */; };
-		907D035415B86F8700575110 /* libosgdb_gdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F715B86F8700575110 /* libosgdb_gdal.a */; };
-		907D035515B86F8700575110 /* libosgdb_geo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F815B86F8700575110 /* libosgdb_geo.a */; };
-		907D035615B86F8700575110 /* libosgdb_glsl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02F915B86F8700575110 /* libosgdb_glsl.a */; };
-		907D035715B86F8700575110 /* libosgdb_gz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FA15B86F8700575110 /* libosgdb_gz.a */; };
-		907D035815B86F8700575110 /* libosgdb_hdr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FB15B86F8700575110 /* libosgdb_hdr.a */; };
-		907D035915B86F8700575110 /* libosgdb_imageio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FC15B86F8700575110 /* libosgdb_imageio.a */; };
-		907D035A15B86F8700575110 /* libosgdb_ive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FD15B86F8700575110 /* libosgdb_ive.a */; };
-		907D035B15B86F8700575110 /* libosgdb_logo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FE15B86F8700575110 /* libosgdb_logo.a */; };
-		907D035C15B86F8700575110 /* libosgdb_lwo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D02FF15B86F8700575110 /* libosgdb_lwo.a */; };
-		907D035D15B86F8700575110 /* libosgdb_lws.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030015B86F8700575110 /* libosgdb_lws.a */; };
-		907D035E15B86F8700575110 /* libosgdb_md2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030115B86F8700575110 /* libosgdb_md2.a */; };
-		907D035F15B86F8700575110 /* libosgdb_mdl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030215B86F8700575110 /* libosgdb_mdl.a */; };
-		907D036015B86F8700575110 /* libosgdb_normals.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030315B86F8700575110 /* libosgdb_normals.a */; };
-		907D036115B86F8700575110 /* libosgdb_obj.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030415B86F8700575110 /* libosgdb_obj.a */; };
-		907D036215B86F8700575110 /* libosgdb_ogr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030515B86F8700575110 /* libosgdb_ogr.a */; };
-		907D036315B86F8700575110 /* libosgdb_openflight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030615B86F8700575110 /* libosgdb_openflight.a */; };
-		907D036415B86F8700575110 /* libosgdb_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030715B86F8700575110 /* libosgdb_osg.a */; };
-		907D036515B86F8700575110 /* libosgdb_osga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030815B86F8700575110 /* libosgdb_osga.a */; };
-		907D036615B86F8700575110 /* libosgdb_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030915B86F8700575110 /* libosgdb_osgshadow.a */; };
-		907D036715B86F8700575110 /* libosgdb_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030A15B86F8700575110 /* libosgdb_osgterrain.a */; };
-		907D036815B86F8700575110 /* libosgdb_osgtgz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030B15B86F8700575110 /* libosgdb_osgtgz.a */; };
-		907D036915B86F8700575110 /* libosgdb_osgviewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030C15B86F8700575110 /* libosgdb_osgviewer.a */; };
-		907D036A15B86F8700575110 /* libosgdb_p3d.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030D15B86F8700575110 /* libosgdb_p3d.a */; };
-		907D036B15B86F8700575110 /* libosgdb_pic.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030E15B86F8700575110 /* libosgdb_pic.a */; };
-		907D036C15B86F8700575110 /* libosgdb_ply.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D030F15B86F8700575110 /* libosgdb_ply.a */; };
-		907D036D15B86F8700575110 /* libosgdb_pnm.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031015B86F8700575110 /* libosgdb_pnm.a */; };
-		907D036E15B86F8700575110 /* libosgdb_pov.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031115B86F8700575110 /* libosgdb_pov.a */; };
-		907D036F15B86F8700575110 /* libosgdb_pvr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031215B86F8700575110 /* libosgdb_pvr.a */; };
-		907D037015B86F8700575110 /* libosgdb_revisions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031315B86F8700575110 /* libosgdb_revisions.a */; };
-		907D037115B86F8700575110 /* libosgdb_rgb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031415B86F8700575110 /* libosgdb_rgb.a */; };
-		907D037215B86F8700575110 /* libosgdb_rot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031515B86F8700575110 /* libosgdb_rot.a */; };
-		907D037315B86F8700575110 /* libosgdb_scale.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031615B86F8700575110 /* libosgdb_scale.a */; };
-		907D037415B86F8700575110 /* libosgdb_serializers_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031715B86F8700575110 /* libosgdb_serializers_osg.a */; };
-		907D037515B86F8700575110 /* libosgdb_serializers_osganimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */; };
-		907D037615B86F8700575110 /* libosgdb_serializers_osgfx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */; };
-		907D037715B86F8700575110 /* libosgdb_serializers_osgmanipulator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */; };
-		907D037815B86F8700575110 /* libosgdb_serializers_osgparticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */; };
-		907D037915B86F8700575110 /* libosgdb_serializers_osgshadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */; };
-		907D037A15B86F8700575110 /* libosgdb_serializers_osgsim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */; };
-		907D037B15B86F8700575110 /* libosgdb_serializers_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */; };
-		907D037C15B86F8700575110 /* libosgdb_serializers_osgtext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */; };
-		907D037D15B86F8700575110 /* libosgdb_serializers_osgvolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */; };
-		907D037E15B86F8700575110 /* libosgdb_shp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032115B86F8700575110 /* libosgdb_shp.a */; };
-		907D037F15B86F8700575110 /* libosgdb_stl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032215B86F8700575110 /* libosgdb_stl.a */; };
-		907D038015B86F8700575110 /* libosgdb_tga.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032315B86F8700575110 /* libosgdb_tga.a */; };
-		907D038115B86F8700575110 /* libosgdb_tgz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032415B86F8700575110 /* libosgdb_tgz.a */; };
-		907D038215B86F8700575110 /* libosgdb_trans.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032515B86F8700575110 /* libosgdb_trans.a */; };
-		907D038315B86F8700575110 /* libosgdb_txf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032615B86F8700575110 /* libosgdb_txf.a */; };
-		907D038415B86F8700575110 /* libosgdb_txp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032715B86F8700575110 /* libosgdb_txp.a */; };
-		907D038515B86F8700575110 /* libosgdb_vtf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032815B86F8700575110 /* libosgdb_vtf.a */; };
-		907D038615B86F8700575110 /* libosgdb_x.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032915B86F8700575110 /* libosgdb_x.a */; };
-		907D038715B86F8700575110 /* libosgdb_zip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032A15B86F8700575110 /* libosgdb_zip.a */; };
-		907D038815B86F8700575110 /* libosgDB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032B15B86F8700575110 /* libosgDB.a */; };
-		907D038915B86F8700575110 /* libosgFX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032C15B86F8700575110 /* libosgFX.a */; };
-		907D038A15B86F8700575110 /* libosgGA.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032D15B86F8700575110 /* libosgGA.a */; };
-		907D038B15B86F8700575110 /* libosgManipulator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032E15B86F8700575110 /* libosgManipulator.a */; };
-		907D038C15B86F8700575110 /* libosgParticle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D032F15B86F8700575110 /* libosgParticle.a */; };
-		907D038D15B86F8700575110 /* libosgPresentation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033015B86F8700575110 /* libosgPresentation.a */; };
-		907D038E15B86F8700575110 /* libosgShadow.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033115B86F8700575110 /* libosgShadow.a */; };
-		907D038F15B86F8700575110 /* libosgSim.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033215B86F8700575110 /* libosgSim.a */; };
-		907D039015B86F8700575110 /* libosgTerrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033315B86F8700575110 /* libosgTerrain.a */; };
-		907D039115B86F8700575110 /* libosgText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033415B86F8700575110 /* libosgText.a */; };
-		907D039215B86F8700575110 /* libosgUtil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033515B86F8700575110 /* libosgUtil.a */; };
-		907D039315B86F8700575110 /* libosgViewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033615B86F8700575110 /* libosgViewer.a */; };
-		907D039415B86F8700575110 /* libosgVolume.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033715B86F8700575110 /* libosgVolume.a */; };
-		907D039515B86F8700575110 /* libosgWidget.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D033815B86F8700575110 /* libosgWidget.a */; };
-		907D03BD15B86F9E00575110 /* libosgdb_kml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039715B86F9E00575110 /* libosgdb_kml.a */; };
-		907D03BE15B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */; };
-		907D03BF15B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */; };
-		907D03C015B86F9E00575110 /* libosgdb_osgearth_tms.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */; };
-		907D03C115B86F9E00575110 /* libosgdb_osgearth_wms.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */; };
-		907D03C215B86F9E00575110 /* libosgdb_osgearth_label_overlay.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */; };
-		907D03C315B86F9E00575110 /* libosgdb_osgearth_xyz.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */; };
-		907D03C415B86F9E00575110 /* libosgEarthUtil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039E15B86F9E00575110 /* libosgEarthUtil.a */; };
-		907D03C515B86F9E00575110 /* libosgdb_osgearth_label_annotation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */; };
-		907D03C615B86F9E00575110 /* libosgdb_osgearth_mask_feature.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */; };
-		907D03C715B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */; };
-		907D03C815B86F9E00575110 /* libosgEarthAnnotation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */; };
-		907D03C915B86F9E00575110 /* libosgdb_osgearth_agglite.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */; };
-		907D03CA15B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */; };
-		907D03CB15B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */; };
-		907D03CC15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */; };
-		907D03CD15B86F9E00575110 /* libosgdb_osgearth_model_simple.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */; };
-		907D03CE15B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */; };
-		907D03CF15B86F9E00575110 /* libosgEarthFeatures.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03A915B86F9E00575110 /* libosgEarthFeatures.a */; };
-		907D03D015B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */; };
-		907D03D115B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */; };
-		907D03D215B86F9E00575110 /* libosgdb_osgearth_debug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */; };
-		907D03D315B86F9E00575110 /* libosgdb_osgearth_mbtiles.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */; };
-		907D03D415B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */; };
-		907D03D515B86F9E00575110 /* libosgdb_osgearth_tileservice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */; };
-		907D03D615B86F9E00575110 /* libosgdb_osgearth_yahoo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */; };
-		907D03D715B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */; };
-		907D03D815B86F9E00575110 /* libosgdb_osgearth_tilecache.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */; };
-		907D03D915B86F9E00575110 /* libosgdb_osgearth_wcs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */; };
-		907D03DA15B86F9E00575110 /* libosgEarthSymbology.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B415B86F9E00575110 /* libosgEarthSymbology.a */; };
-		907D03DB15B86F9F00575110 /* libosgdb_osgearth_gdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */; };
-		907D03DC15B86F9F00575110 /* libosgdb_osgearth_refresh.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */; };
-		907D03DD15B86F9F00575110 /* libosgdb_osgearth_vpb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */; };
-		907D03DE15B86F9F00575110 /* libosgdb_earth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B815B86F9E00575110 /* libosgdb_earth.a */; };
-		907D03DF15B86F9F00575110 /* libosgdb_osgearth_cache_filesystem.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */; };
-		907D03E015B86F9F00575110 /* libosgdb_osgearth_arcgis.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */; };
-		907D03E115B86F9F00575110 /* libosgdb_osgearth_osg.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */; };
-		907D03E215B86F9F00575110 /* libosgEarth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03BC15B86F9E00575110 /* libosgEarth.a */; };
-		907D03FA15B8C31A00575110 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D03F915B8C31A00575110 /* libsqlite3.dylib */; };
-		907D0A8E15B8CEBE00575110 /* libGEOS_3.2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */; };
-		907D0A9115B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */; };
-		90A0DD6D15B7BAF9004FACEE /* libFreeType_iphone_universal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */; };
-		90A0DD6F15B7BB50004FACEE /* libproj.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD6E15B7BB50004FACEE /* libproj.a */; };
-		90A0DD7115B7BB64004FACEE /* libgdal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD7015B7BB64004FACEE /* libgdal.a */; };
-		90A0DD7615B7BBA4004FACEE /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 90A0DD7515B7BBA4004FACEE /* libcurl.a */; };
-		90B8676615C8894900F5CDC3 /* StartViewerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 90B8676415C8894900F5CDC3 /* StartViewerController.m */; };
-		90B8676715C8894900F5CDC3 /* StartViewerController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 90B8676515C8894900F5CDC3 /* StartViewerController.xib */; };
-		90DABDDC15CEFF9700D0F609 /* moon_1024x512.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXFileReference section */
-		1F823FFD173C1D20003B519D /* libosgdb_osgearth_engine_mp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_engine_mp.a; path = ../../../lib/Release/libosgdb_osgearth_engine_mp.a; sourceTree = "<group>"; };
-		1F8566A717666C29005BCD2B /* libosgdb_osgearth_scriptengine_javascriptcore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_scriptengine_javascriptcore.a; path = ../../../lib/Release/libosgdb_osgearth_scriptengine_javascriptcore.a; sourceTree = "<group>"; };
-		1F8566A917666E67005BCD2B /* libJavaScriptCore.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libJavaScriptCore.a; path = ../../../../3rdParty/all/libJavaScriptCore.a; sourceTree = "<group>"; };
-		1F8566AB17666EC1005BCD2B /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
-		1FA5C121177B608000A161FF /* gdal_data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = gdal_data; path = ../../../../../3rdParty/gdal_data; sourceTree = "<group>"; };
-		90283E5415C6FB3E00620EEF /* tests */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tests; path = ../../../../tests; sourceTree = "<group>"; };
-		90283E5515C6FB3E00620EEF /* data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = data; path = ../../../../data; sourceTree = "<group>"; };
-		90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_engine_quadtree.a; path = ../../../lib/Release/libosgdb_osgearth_engine_quadtree.a; sourceTree = "<group>"; };
-		903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EarthMultiTouchManipulator.cpp; sourceTree = "<group>"; };
-		903B45DA15C0DE9F00F7702B /* EarthMultiTouchManipulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EarthMultiTouchManipulator.h; sourceTree = "<group>"; };
-		9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tiff.a; path = "../../../../osg-ios/lib/libosgdb_tiff.a"; sourceTree = "<group>"; };
-		904E22751691CC42002D66FD /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
-		904E22761691CC42002D66FD /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
-		9051000015B1EDFD00D9ABD3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
-		9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
-		9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
-		9051000A15B1EDFD00D9ABD3 /* osgEarthViewerIOS-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "osgEarthViewerIOS-Info.plist"; sourceTree = "<group>"; };
-		9051000C15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		9051000E15B1EDFD00D9ABD3 /* main.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = main.m; sourceTree = "<group>"; };
-		9051001015B1EDFD00D9ABD3 /* osgEarthViewerIOS-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "osgEarthViewerIOS-Prefix.pch"; sourceTree = "<group>"; };
-		9051001115B1EDFD00D9ABD3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
-		9051001215B1EDFD00D9ABD3 /* AppDelegate.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = AppDelegate.m; sourceTree = "<group>"; };
-		9051001815B1EDFD00D9ABD3 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
-		9051001915B1EDFD00D9ABD3 /* ViewController.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = ViewController.m; sourceTree = "<group>"; };
-		9051001C15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPhone.xib; sourceTree = "<group>"; };
-		9051001F15B1EDFD00D9ABD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPad.xib; sourceTree = "<group>"; };
-		905100C215B20AC600D9ABD3 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
-		905100C415B20AD100D9ABD3 /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; };
-		905100C615B20B1D00D9ABD3 /* osgPlugins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = osgPlugins.h; sourceTree = "<group>"; };
-		905100CA15B2101E00D9ABD3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
-		905100CC15B217A800D9ABD3 /* libc++.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libc++.dylib"; path = "usr/lib/libc++.dylib"; sourceTree = SDKROOT; };
-		905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.1.3.dylib; path = usr/lib/libz.1.1.3.dylib; sourceTree = SDKROOT; };
-		905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.2.4.0.dylib; path = usr/lib/libiconv.2.4.0.dylib; sourceTree = SDKROOT; };
-		9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = osgEarth.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
-		907D02DC15B86F8700575110 /* libOpenThreads.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libOpenThreads.a; path = "../../../../osg-ios/lib/libOpenThreads.a"; sourceTree = "<group>"; };
-		907D02DD15B86F8700575110 /* libosg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosg.a; path = "../../../../osg-ios/lib/libosg.a"; sourceTree = "<group>"; };
-		907D02DE15B86F8700575110 /* libosgAnimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgAnimation.a; path = "../../../../osg-ios/lib/libosgAnimation.a"; sourceTree = "<group>"; };
-		907D02DF15B86F8700575110 /* libosgdb_3dc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3dc.a; path = "../../../../osg-ios/lib/libosgdb_3dc.a"; sourceTree = "<group>"; };
-		907D02E015B86F8700575110 /* libosgdb_3ds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_3ds.a; path = "../../../../osg-ios/lib/libosgdb_3ds.a"; sourceTree = "<group>"; };
-		907D02E115B86F8700575110 /* libosgdb_ac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ac.a; path = "../../../../osg-ios/lib/libosgdb_ac.a"; sourceTree = "<group>"; };
-		907D02E215B86F8700575110 /* libosgdb_bmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bmp.a; path = "../../../../osg-ios/lib/libosgdb_bmp.a"; sourceTree = "<group>"; };
-		907D02E315B86F8700575110 /* libosgdb_bsp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bsp.a; path = "../../../../osg-ios/lib/libosgdb_bsp.a"; sourceTree = "<group>"; };
-		907D02E415B86F8700575110 /* libosgdb_bvh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_bvh.a; path = "../../../../osg-ios/lib/libosgdb_bvh.a"; sourceTree = "<group>"; };
-		907D02E515B86F8700575110 /* libosgdb_cfg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_cfg.a; path = "../../../../osg-ios/lib/libosgdb_cfg.a"; sourceTree = "<group>"; };
-		907D02E615B86F8700575110 /* libosgdb_curl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_curl.a; path = "../../../../osg-ios/lib/libosgdb_curl.a"; sourceTree = "<group>"; };
-		907D02E715B86F8700575110 /* libosgdb_dds.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dds.a; path = "../../../../osg-ios/lib/libosgdb_dds.a"; sourceTree = "<group>"; };
-		907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osg.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osg.a"; sourceTree = "<group>"; };
-		907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osganimation.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osganimation.a"; sourceTree = "<group>"; };
-		907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgfx.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgfx.a"; sourceTree = "<group>"; };
-		907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgparticle.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgparticle.a"; sourceTree = "<group>"; };
-		907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgshadow.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgshadow.a"; sourceTree = "<group>"; };
-		907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgsim.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgsim.a"; sourceTree = "<group>"; };
-		907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgterrain.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgterrain.a"; sourceTree = "<group>"; };
-		907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgtext.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgtext.a"; sourceTree = "<group>"; };
-		907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgviewer.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgviewer.a"; sourceTree = "<group>"; };
-		907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgvolume.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgvolume.a"; sourceTree = "<group>"; };
-		907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_deprecated_osgwidget.a; path = "../../../../osg-ios/lib/libosgdb_deprecated_osgwidget.a"; sourceTree = "<group>"; };
-		907D02F315B86F8700575110 /* libosgdb_dot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dot.a; path = "../../../../osg-ios/lib/libosgdb_dot.a"; sourceTree = "<group>"; };
-		907D02F415B86F8700575110 /* libosgdb_dw.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dw.a; path = "../../../../osg-ios/lib/libosgdb_dw.a"; sourceTree = "<group>"; };
-		907D02F515B86F8700575110 /* libosgdb_dxf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_dxf.a; path = "../../../../osg-ios/lib/libosgdb_dxf.a"; sourceTree = "<group>"; };
-		907D02F615B86F8700575110 /* libosgdb_freetype.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_freetype.a; path = "../../../../osg-ios/lib/libosgdb_freetype.a"; sourceTree = "<group>"; };
-		907D02F715B86F8700575110 /* libosgdb_gdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gdal.a; path = "../../../../osg-ios/lib/libosgdb_gdal.a"; sourceTree = "<group>"; };
-		907D02F815B86F8700575110 /* libosgdb_geo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_geo.a; path = "../../../../osg-ios/lib/libosgdb_geo.a"; sourceTree = "<group>"; };
-		907D02F915B86F8700575110 /* libosgdb_glsl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_glsl.a; path = "../../../../osg-ios/lib/libosgdb_glsl.a"; sourceTree = "<group>"; };
-		907D02FA15B86F8700575110 /* libosgdb_gz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_gz.a; path = "../../../../osg-ios/lib/libosgdb_gz.a"; sourceTree = "<group>"; };
-		907D02FB15B86F8700575110 /* libosgdb_hdr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_hdr.a; path = "../../../../osg-ios/lib/libosgdb_hdr.a"; sourceTree = "<group>"; };
-		907D02FC15B86F8700575110 /* libosgdb_imageio.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_imageio.a; path = "../../../../osg-ios/lib/libosgdb_imageio.a"; sourceTree = "<group>"; };
-		907D02FD15B86F8700575110 /* libosgdb_ive.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ive.a; path = "../../../../osg-ios/lib/libosgdb_ive.a"; sourceTree = "<group>"; };
-		907D02FE15B86F8700575110 /* libosgdb_logo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_logo.a; path = "../../../../osg-ios/lib/libosgdb_logo.a"; sourceTree = "<group>"; };
-		907D02FF15B86F8700575110 /* libosgdb_lwo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lwo.a; path = "../../../../osg-ios/lib/libosgdb_lwo.a"; sourceTree = "<group>"; };
-		907D030015B86F8700575110 /* libosgdb_lws.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_lws.a; path = "../../../../osg-ios/lib/libosgdb_lws.a"; sourceTree = "<group>"; };
-		907D030115B86F8700575110 /* libosgdb_md2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_md2.a; path = "../../../../osg-ios/lib/libosgdb_md2.a"; sourceTree = "<group>"; };
-		907D030215B86F8700575110 /* libosgdb_mdl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_mdl.a; path = "../../../../osg-ios/lib/libosgdb_mdl.a"; sourceTree = "<group>"; };
-		907D030315B86F8700575110 /* libosgdb_normals.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_normals.a; path = "../../../../osg-ios/lib/libosgdb_normals.a"; sourceTree = "<group>"; };
-		907D030415B86F8700575110 /* libosgdb_obj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_obj.a; path = "../../../../osg-ios/lib/libosgdb_obj.a"; sourceTree = "<group>"; };
-		907D030515B86F8700575110 /* libosgdb_ogr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ogr.a; path = "../../../../osg-ios/lib/libosgdb_ogr.a"; sourceTree = "<group>"; };
-		907D030615B86F8700575110 /* libosgdb_openflight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_openflight.a; path = "../../../../osg-ios/lib/libosgdb_openflight.a"; sourceTree = "<group>"; };
-		907D030715B86F8700575110 /* libosgdb_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osg.a; path = "../../../../osg-ios/lib/libosgdb_osg.a"; sourceTree = "<group>"; };
-		907D030815B86F8700575110 /* libosgdb_osga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osga.a; path = "../../../../osg-ios/lib/libosgdb_osga.a"; sourceTree = "<group>"; };
-		907D030915B86F8700575110 /* libosgdb_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgshadow.a; path = "../../../../osg-ios/lib/libosgdb_osgshadow.a"; sourceTree = "<group>"; };
-		907D030A15B86F8700575110 /* libosgdb_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgterrain.a; path = "../../../../osg-ios/lib/libosgdb_osgterrain.a"; sourceTree = "<group>"; };
-		907D030B15B86F8700575110 /* libosgdb_osgtgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgtgz.a; path = "../../../../osg-ios/lib/libosgdb_osgtgz.a"; sourceTree = "<group>"; };
-		907D030C15B86F8700575110 /* libosgdb_osgviewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgviewer.a; path = "../../../../osg-ios/lib/libosgdb_osgviewer.a"; sourceTree = "<group>"; };
-		907D030D15B86F8700575110 /* libosgdb_p3d.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_p3d.a; path = "../../../../osg-ios/lib/libosgdb_p3d.a"; sourceTree = "<group>"; };
-		907D030E15B86F8700575110 /* libosgdb_pic.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pic.a; path = "../../../../osg-ios/lib/libosgdb_pic.a"; sourceTree = "<group>"; };
-		907D030F15B86F8700575110 /* libosgdb_ply.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_ply.a; path = "../../../../osg-ios/lib/libosgdb_ply.a"; sourceTree = "<group>"; };
-		907D031015B86F8700575110 /* libosgdb_pnm.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pnm.a; path = "../../../../osg-ios/lib/libosgdb_pnm.a"; sourceTree = "<group>"; };
-		907D031115B86F8700575110 /* libosgdb_pov.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pov.a; path = "../../../../osg-ios/lib/libosgdb_pov.a"; sourceTree = "<group>"; };
-		907D031215B86F8700575110 /* libosgdb_pvr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_pvr.a; path = "../../../../osg-ios/lib/libosgdb_pvr.a"; sourceTree = "<group>"; };
-		907D031315B86F8700575110 /* libosgdb_revisions.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_revisions.a; path = "../../../../osg-ios/lib/libosgdb_revisions.a"; sourceTree = "<group>"; };
-		907D031415B86F8700575110 /* libosgdb_rgb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rgb.a; path = "../../../../osg-ios/lib/libosgdb_rgb.a"; sourceTree = "<group>"; };
-		907D031515B86F8700575110 /* libosgdb_rot.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_rot.a; path = "../../../../osg-ios/lib/libosgdb_rot.a"; sourceTree = "<group>"; };
-		907D031615B86F8700575110 /* libosgdb_scale.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_scale.a; path = "../../../../osg-ios/lib/libosgdb_scale.a"; sourceTree = "<group>"; };
-		907D031715B86F8700575110 /* libosgdb_serializers_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osg.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osg.a"; sourceTree = "<group>"; };
-		907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osganimation.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osganimation.a"; sourceTree = "<group>"; };
-		907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgfx.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgfx.a"; sourceTree = "<group>"; };
-		907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgmanipulator.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgmanipulator.a"; sourceTree = "<group>"; };
-		907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgparticle.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgparticle.a"; sourceTree = "<group>"; };
-		907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgshadow.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgshadow.a"; sourceTree = "<group>"; };
-		907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgsim.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgsim.a"; sourceTree = "<group>"; };
-		907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgterrain.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgterrain.a"; sourceTree = "<group>"; };
-		907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgtext.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgtext.a"; sourceTree = "<group>"; };
-		907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_serializers_osgvolume.a; path = "../../../../osg-ios/lib/libosgdb_serializers_osgvolume.a"; sourceTree = "<group>"; };
-		907D032115B86F8700575110 /* libosgdb_shp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_shp.a; path = "../../../../osg-ios/lib/libosgdb_shp.a"; sourceTree = "<group>"; };
-		907D032215B86F8700575110 /* libosgdb_stl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_stl.a; path = "../../../../osg-ios/lib/libosgdb_stl.a"; sourceTree = "<group>"; };
-		907D032315B86F8700575110 /* libosgdb_tga.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tga.a; path = "../../../../osg-ios/lib/libosgdb_tga.a"; sourceTree = "<group>"; };
-		907D032415B86F8700575110 /* libosgdb_tgz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_tgz.a; path = "../../../../osg-ios/lib/libosgdb_tgz.a"; sourceTree = "<group>"; };
-		907D032515B86F8700575110 /* libosgdb_trans.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_trans.a; path = "../../../../osg-ios/lib/libosgdb_trans.a"; sourceTree = "<group>"; };
-		907D032615B86F8700575110 /* libosgdb_txf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txf.a; path = "../../../../osg-ios/lib/libosgdb_txf.a"; sourceTree = "<group>"; };
-		907D032715B86F8700575110 /* libosgdb_txp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_txp.a; path = "../../../../osg-ios/lib/libosgdb_txp.a"; sourceTree = "<group>"; };
-		907D032815B86F8700575110 /* libosgdb_vtf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_vtf.a; path = "../../../../osg-ios/lib/libosgdb_vtf.a"; sourceTree = "<group>"; };
-		907D032915B86F8700575110 /* libosgdb_x.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_x.a; path = "../../../../osg-ios/lib/libosgdb_x.a"; sourceTree = "<group>"; };
-		907D032A15B86F8700575110 /* libosgdb_zip.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_zip.a; path = "../../../../osg-ios/lib/libosgdb_zip.a"; sourceTree = "<group>"; };
-		907D032B15B86F8700575110 /* libosgDB.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgDB.a; path = "../../../../osg-ios/lib/libosgDB.a"; sourceTree = "<group>"; };
-		907D032C15B86F8700575110 /* libosgFX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgFX.a; path = "../../../../osg-ios/lib/libosgFX.a"; sourceTree = "<group>"; };
-		907D032D15B86F8700575110 /* libosgGA.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgGA.a; path = "../../../../osg-ios/lib/libosgGA.a"; sourceTree = "<group>"; };
-		907D032E15B86F8700575110 /* libosgManipulator.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgManipulator.a; path = "../../../../osg-ios/lib/libosgManipulator.a"; sourceTree = "<group>"; };
-		907D032F15B86F8700575110 /* libosgParticle.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgParticle.a; path = "../../../../osg-ios/lib/libosgParticle.a"; sourceTree = "<group>"; };
-		907D033015B86F8700575110 /* libosgPresentation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgPresentation.a; path = "../../../../osg-ios/lib/libosgPresentation.a"; sourceTree = "<group>"; };
-		907D033115B86F8700575110 /* libosgShadow.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgShadow.a; path = "../../../../osg-ios/lib/libosgShadow.a"; sourceTree = "<group>"; };
-		907D033215B86F8700575110 /* libosgSim.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgSim.a; path = "../../../../osg-ios/lib/libosgSim.a"; sourceTree = "<group>"; };
-		907D033315B86F8700575110 /* libosgTerrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgTerrain.a; path = "../../../../osg-ios/lib/libosgTerrain.a"; sourceTree = "<group>"; };
-		907D033415B86F8700575110 /* libosgText.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgText.a; path = "../../../../osg-ios/lib/libosgText.a"; sourceTree = "<group>"; };
-		907D033515B86F8700575110 /* libosgUtil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgUtil.a; path = "../../../../osg-ios/lib/libosgUtil.a"; sourceTree = "<group>"; };
-		907D033615B86F8700575110 /* libosgViewer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgViewer.a; path = "../../../../osg-ios/lib/libosgViewer.a"; sourceTree = "<group>"; };
-		907D033715B86F8700575110 /* libosgVolume.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgVolume.a; path = "../../../../osg-ios/lib/libosgVolume.a"; sourceTree = "<group>"; };
-		907D033815B86F8700575110 /* libosgWidget.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgWidget.a; path = "../../../../osg-ios/lib/libosgWidget.a"; sourceTree = "<group>"; };
-		907D039715B86F9E00575110 /* libosgdb_kml.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_kml.a; path = ../../../lib/Release/libosgdb_kml.a; sourceTree = "<group>"; };
-		907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_wfs.a; path = ../../../lib/Release/libosgdb_osgearth_feature_wfs.a; sourceTree = "<group>"; };
-		907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_tfs.a; path = ../../../lib/Release/libosgdb_osgearth_feature_tfs.a; sourceTree = "<group>"; };
-		907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tms.a; path = ../../../lib/Release/libosgdb_osgearth_tms.a; sourceTree = "<group>"; };
-		907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_wms.a; path = ../../../lib/Release/libosgdb_osgearth_wms.a; sourceTree = "<group>"; };
-		907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_label_overlay.a; path = ../../../lib/Release/libosgdb_osgearth_label_overlay.a; sourceTree = "<group>"; };
-		907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_xyz.a; path = ../../../lib/Release/libosgdb_osgearth_xyz.a; sourceTree = "<group>"; };
-		907D039E15B86F9E00575110 /* libosgEarthUtil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthUtil.a; path = ../../../lib/Release/libosgEarthUtil.a; sourceTree = "<group>"; };
-		907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_label_annotation.a; path = ../../../lib/Release/libosgdb_osgearth_label_annotation.a; sourceTree = "<group>"; };
-		907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_mask_feature.a; path = ../../../lib/Release/libosgdb_osgearth_mask_feature.a; sourceTree = "<group>"; };
-		907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_feature_geom.a; path = ../../../lib/Release/libosgdb_osgearth_model_feature_geom.a; sourceTree = "<group>"; };
-		907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthAnnotation.a; path = ../../../lib/Release/libosgEarthAnnotation.a; sourceTree = "<group>"; };
-		907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_agglite.a; path = ../../../lib/Release/libosgdb_osgearth_agglite.a; sourceTree = "<group>"; };
-		907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_feature_ogr.a; path = ../../../lib/Release/libosgdb_osgearth_feature_ogr.a; sourceTree = "<group>"; };
-		907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_feature_stencil.a; path = ../../../lib/Release/libosgdb_osgearth_model_feature_stencil.a; sourceTree = "<group>"; };
-		907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm2008.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm2008.a; sourceTree = "<group>"; };
-		907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_model_simple.a; path = ../../../lib/Release/libosgdb_osgearth_model_simple.a; sourceTree = "<group>"; };
-		907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_engine_osgterrain.a; path = ../../../lib/Release/libosgdb_osgearth_engine_osgterrain.a; sourceTree = "<group>"; };
-		907D03A915B86F9E00575110 /* libosgEarthFeatures.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthFeatures.a; path = ../../../lib/Release/libosgEarthFeatures.a; sourceTree = "<group>"; };
-		907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm96.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm96.a; sourceTree = "<group>"; };
-		907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_ocean_surface.a; path = ../../../lib/Release/libosgdb_osgearth_ocean_surface.a; sourceTree = "<group>"; };
-		907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_debug.a; path = ../../../lib/Release/libosgdb_osgearth_debug.a; sourceTree = "<group>"; };
-		907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_mbtiles.a; path = ../../../lib/Release/libosgdb_osgearth_mbtiles.a; sourceTree = "<group>"; };
-		907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vdatum_egm84.a; path = ../../../lib/Release/libosgdb_osgearth_vdatum_egm84.a; sourceTree = "<group>"; };
-		907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tileservice.a; path = ../../../lib/Release/libosgdb_osgearth_tileservice.a; sourceTree = "<group>"; };
-		907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_yahoo.a; path = ../../../lib/Release/libosgdb_osgearth_yahoo.a; sourceTree = "<group>"; };
-		907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_arcgis_map_cache.a; path = ../../../lib/Release/libosgdb_osgearth_arcgis_map_cache.a; sourceTree = "<group>"; };
-		907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_tilecache.a; path = ../../../lib/Release/libosgdb_osgearth_tilecache.a; sourceTree = "<group>"; };
-		907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_wcs.a; path = ../../../lib/Release/libosgdb_osgearth_wcs.a; sourceTree = "<group>"; };
-		907D03B415B86F9E00575110 /* libosgEarthSymbology.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarthSymbology.a; path = ../../../lib/Release/libosgEarthSymbology.a; sourceTree = "<group>"; };
-		907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_gdal.a; path = ../../../lib/Release/libosgdb_osgearth_gdal.a; sourceTree = "<group>"; };
-		907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_refresh.a; path = ../../../lib/Release/libosgdb_osgearth_refresh.a; sourceTree = "<group>"; };
-		907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_vpb.a; path = ../../../lib/Release/libosgdb_osgearth_vpb.a; sourceTree = "<group>"; };
-		907D03B815B86F9E00575110 /* libosgdb_earth.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_earth.a; path = ../../../lib/Release/libosgdb_earth.a; sourceTree = "<group>"; };
-		907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_cache_filesystem.a; path = ../../../lib/Release/libosgdb_osgearth_cache_filesystem.a; sourceTree = "<group>"; };
-		907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_arcgis.a; path = ../../../lib/Release/libosgdb_osgearth_arcgis.a; sourceTree = "<group>"; };
-		907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgdb_osgearth_osg.a; path = ../../../lib/Release/libosgdb_osgearth_osg.a; sourceTree = "<group>"; };
-		907D03BC15B86F9E00575110 /* libosgEarth.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libosgEarth.a; path = ../../../lib/Release/libosgEarth.a; sourceTree = "<group>"; };
-		907D03F915B8C31A00575110 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
-		907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libGEOS_3.2.a; path = "../../../../3rdParty/geos-ios-device/lib/libGEOS_3.2.a"; sourceTree = "<group>"; };
-		907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = GLES2ShaderGenVisitor.cpp; path = ShaderGen/GLES2ShaderGenVisitor.cpp; sourceTree = "<group>"; };
-		907D0A9015B8DDAA00575110 /* GLES2ShaderGenVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GLES2ShaderGenVisitor.h; path = ShaderGen/GLES2ShaderGenVisitor.h; sourceTree = "<group>"; };
-		90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libFreeType_iphone_universal.a; sourceTree = "<group>"; };
-		90A0DD6E15B7BB50004FACEE /* libproj.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libproj.a; sourceTree = "<group>"; };
-		90A0DD7015B7BB64004FACEE /* libgdal.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgdal.a; sourceTree = "<group>"; };
-		90A0DD7515B7BBA4004FACEE /* libcurl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libcurl.a; sourceTree = "<group>"; };
-		90B8676315C8894900F5CDC3 /* StartViewerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StartViewerController.h; sourceTree = "<group>"; };
-		90B8676415C8894900F5CDC3 /* StartViewerController.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = StartViewerController.m; sourceTree = "<group>"; };
-		90B8676515C8894900F5CDC3 /* StartViewerController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StartViewerController.xib; sourceTree = "<group>"; };
-		90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; name = moon_1024x512.jpg; path = ../../../../data/moon_1024x512.jpg; sourceTree = "<group>"; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
-		9051FFF715B1EDFD00D9ABD3 /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				1F8566AC17666EC1005BCD2B /* libicucore.dylib in Frameworks */,
-				1F8566AA17666E67005BCD2B /* libJavaScriptCore.a in Frameworks */,
-				1F8566A817666C29005BCD2B /* libosgdb_osgearth_scriptengine_javascriptcore.a in Frameworks */,
-				1F823FFE173C1D20003B519D /* libosgdb_osgearth_engine_mp.a in Frameworks */,
-				904E22771691CC42002D66FD /* Accelerate.framework in Frameworks */,
-				904E22781691CC42002D66FD /* MobileCoreServices.framework in Frameworks */,
-				907D037E15B86F8700575110 /* libosgdb_shp.a in Frameworks */,
-				9048FB2415FA9DE50012C900 /* libosgdb_tiff.a in Frameworks */,
-				907D03FA15B8C31A00575110 /* libsqlite3.dylib in Frameworks */,
-				90A0DD7615B7BBA4004FACEE /* libcurl.a in Frameworks */,
-				90A0DD7115B7BB64004FACEE /* libgdal.a in Frameworks */,
-				90A0DD6F15B7BB50004FACEE /* libproj.a in Frameworks */,
-				90A0DD6D15B7BAF9004FACEE /* libFreeType_iphone_universal.a in Frameworks */,
-				905100D115B2185000D9ABD3 /* libiconv.2.4.0.dylib in Frameworks */,
-				905100CF15B217B500D9ABD3 /* libz.1.1.3.dylib in Frameworks */,
-				905100CD15B217A800D9ABD3 /* libc++.dylib in Frameworks */,
-				905100CB15B2101E00D9ABD3 /* QuartzCore.framework in Frameworks */,
-				905100C515B20AD100D9ABD3 /* CoreImage.framework in Frameworks */,
-				905100C315B20AC600D9ABD3 /* ImageIO.framework in Frameworks */,
-				9051FFFF15B1EDFD00D9ABD3 /* UIKit.framework in Frameworks */,
-				9051000115B1EDFD00D9ABD3 /* Foundation.framework in Frameworks */,
-				9051000315B1EDFD00D9ABD3 /* CoreGraphics.framework in Frameworks */,
-				9051000715B1EDFD00D9ABD3 /* OpenGLES.framework in Frameworks */,
-				907D033915B86F8700575110 /* libOpenThreads.a in Frameworks */,
-				907D033A15B86F8700575110 /* libosg.a in Frameworks */,
-				907D033B15B86F8700575110 /* libosgAnimation.a in Frameworks */,
-				907D033C15B86F8700575110 /* libosgdb_3dc.a in Frameworks */,
-				907D033D15B86F8700575110 /* libosgdb_3ds.a in Frameworks */,
-				907D033E15B86F8700575110 /* libosgdb_ac.a in Frameworks */,
-				907D033F15B86F8700575110 /* libosgdb_bmp.a in Frameworks */,
-				907D034015B86F8700575110 /* libosgdb_bsp.a in Frameworks */,
-				907D034115B86F8700575110 /* libosgdb_bvh.a in Frameworks */,
-				907D034215B86F8700575110 /* libosgdb_cfg.a in Frameworks */,
-				907D034315B86F8700575110 /* libosgdb_curl.a in Frameworks */,
-				907D034415B86F8700575110 /* libosgdb_dds.a in Frameworks */,
-				907D034515B86F8700575110 /* libosgdb_deprecated_osg.a in Frameworks */,
-				907D034615B86F8700575110 /* libosgdb_deprecated_osganimation.a in Frameworks */,
-				907D034715B86F8700575110 /* libosgdb_deprecated_osgfx.a in Frameworks */,
-				907D034815B86F8700575110 /* libosgdb_deprecated_osgparticle.a in Frameworks */,
-				907D034915B86F8700575110 /* libosgdb_deprecated_osgshadow.a in Frameworks */,
-				907D034A15B86F8700575110 /* libosgdb_deprecated_osgsim.a in Frameworks */,
-				907D034B15B86F8700575110 /* libosgdb_deprecated_osgterrain.a in Frameworks */,
-				907D034C15B86F8700575110 /* libosgdb_deprecated_osgtext.a in Frameworks */,
-				907D034D15B86F8700575110 /* libosgdb_deprecated_osgviewer.a in Frameworks */,
-				907D034E15B86F8700575110 /* libosgdb_deprecated_osgvolume.a in Frameworks */,
-				907D034F15B86F8700575110 /* libosgdb_deprecated_osgwidget.a in Frameworks */,
-				907D035015B86F8700575110 /* libosgdb_dot.a in Frameworks */,
-				907D035115B86F8700575110 /* libosgdb_dw.a in Frameworks */,
-				907D035215B86F8700575110 /* libosgdb_dxf.a in Frameworks */,
-				907D035315B86F8700575110 /* libosgdb_freetype.a in Frameworks */,
-				907D035415B86F8700575110 /* libosgdb_gdal.a in Frameworks */,
-				907D035515B86F8700575110 /* libosgdb_geo.a in Frameworks */,
-				907D035615B86F8700575110 /* libosgdb_glsl.a in Frameworks */,
-				907D035715B86F8700575110 /* libosgdb_gz.a in Frameworks */,
-				907D035815B86F8700575110 /* libosgdb_hdr.a in Frameworks */,
-				907D035915B86F8700575110 /* libosgdb_imageio.a in Frameworks */,
-				907D035A15B86F8700575110 /* libosgdb_ive.a in Frameworks */,
-				907D035B15B86F8700575110 /* libosgdb_logo.a in Frameworks */,
-				907D035C15B86F8700575110 /* libosgdb_lwo.a in Frameworks */,
-				907D035D15B86F8700575110 /* libosgdb_lws.a in Frameworks */,
-				907D035E15B86F8700575110 /* libosgdb_md2.a in Frameworks */,
-				907D035F15B86F8700575110 /* libosgdb_mdl.a in Frameworks */,
-				907D036015B86F8700575110 /* libosgdb_normals.a in Frameworks */,
-				907D036115B86F8700575110 /* libosgdb_obj.a in Frameworks */,
-				907D036215B86F8700575110 /* libosgdb_ogr.a in Frameworks */,
-				907D036315B86F8700575110 /* libosgdb_openflight.a in Frameworks */,
-				907D036415B86F8700575110 /* libosgdb_osg.a in Frameworks */,
-				907D036515B86F8700575110 /* libosgdb_osga.a in Frameworks */,
-				907D036615B86F8700575110 /* libosgdb_osgshadow.a in Frameworks */,
-				907D036715B86F8700575110 /* libosgdb_osgterrain.a in Frameworks */,
-				907D036815B86F8700575110 /* libosgdb_osgtgz.a in Frameworks */,
-				907D036915B86F8700575110 /* libosgdb_osgviewer.a in Frameworks */,
-				907D036A15B86F8700575110 /* libosgdb_p3d.a in Frameworks */,
-				907D036B15B86F8700575110 /* libosgdb_pic.a in Frameworks */,
-				907D036C15B86F8700575110 /* libosgdb_ply.a in Frameworks */,
-				907D036D15B86F8700575110 /* libosgdb_pnm.a in Frameworks */,
-				907D036E15B86F8700575110 /* libosgdb_pov.a in Frameworks */,
-				907D036F15B86F8700575110 /* libosgdb_pvr.a in Frameworks */,
-				907D037015B86F8700575110 /* libosgdb_revisions.a in Frameworks */,
-				907D037115B86F8700575110 /* libosgdb_rgb.a in Frameworks */,
-				907D037215B86F8700575110 /* libosgdb_rot.a in Frameworks */,
-				907D037315B86F8700575110 /* libosgdb_scale.a in Frameworks */,
-				907D037415B86F8700575110 /* libosgdb_serializers_osg.a in Frameworks */,
-				907D037515B86F8700575110 /* libosgdb_serializers_osganimation.a in Frameworks */,
-				907D037615B86F8700575110 /* libosgdb_serializers_osgfx.a in Frameworks */,
-				907D037715B86F8700575110 /* libosgdb_serializers_osgmanipulator.a in Frameworks */,
-				907D037815B86F8700575110 /* libosgdb_serializers_osgparticle.a in Frameworks */,
-				907D037915B86F8700575110 /* libosgdb_serializers_osgshadow.a in Frameworks */,
-				907D037A15B86F8700575110 /* libosgdb_serializers_osgsim.a in Frameworks */,
-				907D037B15B86F8700575110 /* libosgdb_serializers_osgterrain.a in Frameworks */,
-				907D037C15B86F8700575110 /* libosgdb_serializers_osgtext.a in Frameworks */,
-				907D037D15B86F8700575110 /* libosgdb_serializers_osgvolume.a in Frameworks */,
-				907D037F15B86F8700575110 /* libosgdb_stl.a in Frameworks */,
-				907D038015B86F8700575110 /* libosgdb_tga.a in Frameworks */,
-				907D038115B86F8700575110 /* libosgdb_tgz.a in Frameworks */,
-				907D038215B86F8700575110 /* libosgdb_trans.a in Frameworks */,
-				907D038315B86F8700575110 /* libosgdb_txf.a in Frameworks */,
-				907D038415B86F8700575110 /* libosgdb_txp.a in Frameworks */,
-				907D038515B86F8700575110 /* libosgdb_vtf.a in Frameworks */,
-				907D038615B86F8700575110 /* libosgdb_x.a in Frameworks */,
-				907D038715B86F8700575110 /* libosgdb_zip.a in Frameworks */,
-				907D038815B86F8700575110 /* libosgDB.a in Frameworks */,
-				907D038915B86F8700575110 /* libosgFX.a in Frameworks */,
-				907D038A15B86F8700575110 /* libosgGA.a in Frameworks */,
-				907D038B15B86F8700575110 /* libosgManipulator.a in Frameworks */,
-				907D038C15B86F8700575110 /* libosgParticle.a in Frameworks */,
-				907D038D15B86F8700575110 /* libosgPresentation.a in Frameworks */,
-				907D038E15B86F8700575110 /* libosgShadow.a in Frameworks */,
-				907D038F15B86F8700575110 /* libosgSim.a in Frameworks */,
-				907D039015B86F8700575110 /* libosgTerrain.a in Frameworks */,
-				907D039115B86F8700575110 /* libosgText.a in Frameworks */,
-				907D039215B86F8700575110 /* libosgUtil.a in Frameworks */,
-				907D039315B86F8700575110 /* libosgViewer.a in Frameworks */,
-				907D039415B86F8700575110 /* libosgVolume.a in Frameworks */,
-				907D039515B86F8700575110 /* libosgWidget.a in Frameworks */,
-				907D03BD15B86F9E00575110 /* libosgdb_kml.a in Frameworks */,
-				907D03BE15B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a in Frameworks */,
-				907D03BF15B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a in Frameworks */,
-				907D03C015B86F9E00575110 /* libosgdb_osgearth_tms.a in Frameworks */,
-				907D03C115B86F9E00575110 /* libosgdb_osgearth_wms.a in Frameworks */,
-				907D03C215B86F9E00575110 /* libosgdb_osgearth_label_overlay.a in Frameworks */,
-				907D03C315B86F9E00575110 /* libosgdb_osgearth_xyz.a in Frameworks */,
-				907D03C415B86F9E00575110 /* libosgEarthUtil.a in Frameworks */,
-				907D03C515B86F9E00575110 /* libosgdb_osgearth_label_annotation.a in Frameworks */,
-				907D03C615B86F9E00575110 /* libosgdb_osgearth_mask_feature.a in Frameworks */,
-				907D03C715B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a in Frameworks */,
-				907D03C815B86F9E00575110 /* libosgEarthAnnotation.a in Frameworks */,
-				907D03C915B86F9E00575110 /* libosgdb_osgearth_agglite.a in Frameworks */,
-				907D03CA15B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a in Frameworks */,
-				907D03CB15B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a in Frameworks */,
-				907D03CC15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a in Frameworks */,
-				907D03CD15B86F9E00575110 /* libosgdb_osgearth_model_simple.a in Frameworks */,
-				907D03CE15B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a in Frameworks */,
-				907D03CF15B86F9E00575110 /* libosgEarthFeatures.a in Frameworks */,
-				907D03D015B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a in Frameworks */,
-				907D03D115B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a in Frameworks */,
-				907D03D215B86F9E00575110 /* libosgdb_osgearth_debug.a in Frameworks */,
-				907D03D315B86F9E00575110 /* libosgdb_osgearth_mbtiles.a in Frameworks */,
-				907D03D415B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a in Frameworks */,
-				907D03D515B86F9E00575110 /* libosgdb_osgearth_tileservice.a in Frameworks */,
-				907D03D615B86F9E00575110 /* libosgdb_osgearth_yahoo.a in Frameworks */,
-				907D03D715B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a in Frameworks */,
-				907D03D815B86F9E00575110 /* libosgdb_osgearth_tilecache.a in Frameworks */,
-				907D03D915B86F9E00575110 /* libosgdb_osgearth_wcs.a in Frameworks */,
-				907D03DA15B86F9E00575110 /* libosgEarthSymbology.a in Frameworks */,
-				907D03DB15B86F9F00575110 /* libosgdb_osgearth_gdal.a in Frameworks */,
-				907D03DC15B86F9F00575110 /* libosgdb_osgearth_refresh.a in Frameworks */,
-				907D03DD15B86F9F00575110 /* libosgdb_osgearth_vpb.a in Frameworks */,
-				907D03DE15B86F9F00575110 /* libosgdb_earth.a in Frameworks */,
-				907D03DF15B86F9F00575110 /* libosgdb_osgearth_cache_filesystem.a in Frameworks */,
-				907D03E015B86F9F00575110 /* libosgdb_osgearth_arcgis.a in Frameworks */,
-				907D03E115B86F9F00575110 /* libosgdb_osgearth_osg.a in Frameworks */,
-				907D03E215B86F9F00575110 /* libosgEarth.a in Frameworks */,
-				907D0A8E15B8CEBE00575110 /* libGEOS_3.2.a in Frameworks */,
-				90283E5D15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
-		9051000815B1EDFD00D9ABD3 /* osgEarthViewerIOS */ = {
-			isa = PBXGroup;
-			children = (
-				90B8676915C8895100F5CDC3 /* StartView */,
-				908C793D15BB0B4E001CFA5E /* MultiTouchManipulator */,
-				907D0A9215B8DDAF00575110 /* ShaderGen */,
-				905100C615B20B1D00D9ABD3 /* osgPlugins.h */,
-				9051001115B1EDFD00D9ABD3 /* AppDelegate.h */,
-				9051001215B1EDFD00D9ABD3 /* AppDelegate.m */,
-				9051001815B1EDFD00D9ABD3 /* ViewController.h */,
-				9051001915B1EDFD00D9ABD3 /* ViewController.m */,
-				9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */,
-				9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */,
-				9051000915B1EDFD00D9ABD3 /* Supporting Files */,
-			);
-			path = osgEarthViewerIOS;
-			sourceTree = "<group>";
-		};
-		9051000915B1EDFD00D9ABD3 /* Supporting Files */ = {
-			isa = PBXGroup;
-			children = (
-				9051000A15B1EDFD00D9ABD3 /* osgEarthViewerIOS-Info.plist */,
-				9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */,
-				9051000E15B1EDFD00D9ABD3 /* main.m */,
-				9051001015B1EDFD00D9ABD3 /* osgEarthViewerIOS-Prefix.pch */,
-			);
-			name = "Supporting Files";
-			sourceTree = "<group>";
-		};
-		905100D215B21FFD00D9ABD3 /* Resources */ = {
-			isa = PBXGroup;
-			children = (
-				90DABDDB15CEFF9700D0F609 /* moon_1024x512.jpg */,
-				1FA5C121177B608000A161FF /* gdal_data */,
-				90283E5415C6FB3E00620EEF /* tests */,
-				90283E5515C6FB3E00620EEF /* data */,
-			);
-			name = Resources;
-			path = osgEarthViewerIOS;
-			sourceTree = "<group>";
-		};
-		9051FFEF15B1EDFD00D9ABD3 = {
-			isa = PBXGroup;
-			children = (
-				1F8566AB17666EC1005BCD2B /* libicucore.dylib */,
-				1F8566A917666E67005BCD2B /* libJavaScriptCore.a */,
-				1F8566A717666C29005BCD2B /* libosgdb_osgearth_scriptengine_javascriptcore.a */,
-				1F823FFD173C1D20003B519D /* libosgdb_osgearth_engine_mp.a */,
-				9051000815B1EDFD00D9ABD3 /* osgEarthViewerIOS */,
-				905100D215B21FFD00D9ABD3 /* Resources */,
-				90A0DD7715B7BBAB004FACEE /* Libs */,
-				9051FFFB15B1EDFD00D9ABD3 /* Products */,
-			);
-			sourceTree = "<group>";
-		};
-		9051FFFB15B1EDFD00D9ABD3 /* Products */ = {
-			isa = PBXGroup;
-			children = (
-				9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */,
-			);
-			name = Products;
-			sourceTree = "<group>";
-		};
-		9051FFFD15B1EDFD00D9ABD3 /* Frameworks */ = {
-			isa = PBXGroup;
-			children = (
-				904E22751691CC42002D66FD /* Accelerate.framework */,
-				904E22761691CC42002D66FD /* MobileCoreServices.framework */,
-				905100CA15B2101E00D9ABD3 /* QuartzCore.framework */,
-				905100C415B20AD100D9ABD3 /* CoreImage.framework */,
-				905100C215B20AC600D9ABD3 /* ImageIO.framework */,
-				9051FFFE15B1EDFD00D9ABD3 /* UIKit.framework */,
-				9051000015B1EDFD00D9ABD3 /* Foundation.framework */,
-				9051000215B1EDFD00D9ABD3 /* CoreGraphics.framework */,
-				9051000615B1EDFD00D9ABD3 /* OpenGLES.framework */,
-			);
-			name = Frameworks;
-			sourceTree = "<group>";
-		};
-		907D02DB15B86F7100575110 /* osg */ = {
-			isa = PBXGroup;
-			children = (
-				907D02DC15B86F8700575110 /* libOpenThreads.a */,
-				907D02DD15B86F8700575110 /* libosg.a */,
-				907D02DE15B86F8700575110 /* libosgAnimation.a */,
-				907D02DF15B86F8700575110 /* libosgdb_3dc.a */,
-				907D02E015B86F8700575110 /* libosgdb_3ds.a */,
-				907D02E115B86F8700575110 /* libosgdb_ac.a */,
-				907D02E215B86F8700575110 /* libosgdb_bmp.a */,
-				907D02E315B86F8700575110 /* libosgdb_bsp.a */,
-				907D02E415B86F8700575110 /* libosgdb_bvh.a */,
-				907D02E515B86F8700575110 /* libosgdb_cfg.a */,
-				907D02E615B86F8700575110 /* libosgdb_curl.a */,
-				907D02E715B86F8700575110 /* libosgdb_dds.a */,
-				9048FB2315FA9DE50012C900 /* libosgdb_tiff.a */,
-				907D02E815B86F8700575110 /* libosgdb_deprecated_osg.a */,
-				907D02E915B86F8700575110 /* libosgdb_deprecated_osganimation.a */,
-				907D02EA15B86F8700575110 /* libosgdb_deprecated_osgfx.a */,
-				907D02EB15B86F8700575110 /* libosgdb_deprecated_osgparticle.a */,
-				907D02EC15B86F8700575110 /* libosgdb_deprecated_osgshadow.a */,
-				907D02ED15B86F8700575110 /* libosgdb_deprecated_osgsim.a */,
-				907D02EE15B86F8700575110 /* libosgdb_deprecated_osgterrain.a */,
-				907D02EF15B86F8700575110 /* libosgdb_deprecated_osgtext.a */,
-				907D02F015B86F8700575110 /* libosgdb_deprecated_osgviewer.a */,
-				907D02F115B86F8700575110 /* libosgdb_deprecated_osgvolume.a */,
-				907D02F215B86F8700575110 /* libosgdb_deprecated_osgwidget.a */,
-				907D02F315B86F8700575110 /* libosgdb_dot.a */,
-				907D02F415B86F8700575110 /* libosgdb_dw.a */,
-				907D02F515B86F8700575110 /* libosgdb_dxf.a */,
-				907D02F615B86F8700575110 /* libosgdb_freetype.a */,
-				907D02F715B86F8700575110 /* libosgdb_gdal.a */,
-				907D02F815B86F8700575110 /* libosgdb_geo.a */,
-				907D02F915B86F8700575110 /* libosgdb_glsl.a */,
-				907D02FA15B86F8700575110 /* libosgdb_gz.a */,
-				907D02FB15B86F8700575110 /* libosgdb_hdr.a */,
-				907D02FC15B86F8700575110 /* libosgdb_imageio.a */,
-				907D02FD15B86F8700575110 /* libosgdb_ive.a */,
-				907D02FE15B86F8700575110 /* libosgdb_logo.a */,
-				907D02FF15B86F8700575110 /* libosgdb_lwo.a */,
-				907D030015B86F8700575110 /* libosgdb_lws.a */,
-				907D030115B86F8700575110 /* libosgdb_md2.a */,
-				907D030215B86F8700575110 /* libosgdb_mdl.a */,
-				907D030315B86F8700575110 /* libosgdb_normals.a */,
-				907D030415B86F8700575110 /* libosgdb_obj.a */,
-				907D030515B86F8700575110 /* libosgdb_ogr.a */,
-				907D030615B86F8700575110 /* libosgdb_openflight.a */,
-				907D030715B86F8700575110 /* libosgdb_osg.a */,
-				907D030815B86F8700575110 /* libosgdb_osga.a */,
-				907D030915B86F8700575110 /* libosgdb_osgshadow.a */,
-				907D030A15B86F8700575110 /* libosgdb_osgterrain.a */,
-				907D030B15B86F8700575110 /* libosgdb_osgtgz.a */,
-				907D030C15B86F8700575110 /* libosgdb_osgviewer.a */,
-				907D030D15B86F8700575110 /* libosgdb_p3d.a */,
-				907D030E15B86F8700575110 /* libosgdb_pic.a */,
-				907D030F15B86F8700575110 /* libosgdb_ply.a */,
-				907D031015B86F8700575110 /* libosgdb_pnm.a */,
-				907D031115B86F8700575110 /* libosgdb_pov.a */,
-				907D031215B86F8700575110 /* libosgdb_pvr.a */,
-				907D031315B86F8700575110 /* libosgdb_revisions.a */,
-				907D031415B86F8700575110 /* libosgdb_rgb.a */,
-				907D031515B86F8700575110 /* libosgdb_rot.a */,
-				907D031615B86F8700575110 /* libosgdb_scale.a */,
-				907D031715B86F8700575110 /* libosgdb_serializers_osg.a */,
-				907D031815B86F8700575110 /* libosgdb_serializers_osganimation.a */,
-				907D031915B86F8700575110 /* libosgdb_serializers_osgfx.a */,
-				907D031A15B86F8700575110 /* libosgdb_serializers_osgmanipulator.a */,
-				907D031B15B86F8700575110 /* libosgdb_serializers_osgparticle.a */,
-				907D031C15B86F8700575110 /* libosgdb_serializers_osgshadow.a */,
-				907D031D15B86F8700575110 /* libosgdb_serializers_osgsim.a */,
-				907D031E15B86F8700575110 /* libosgdb_serializers_osgterrain.a */,
-				907D031F15B86F8700575110 /* libosgdb_serializers_osgtext.a */,
-				907D032015B86F8700575110 /* libosgdb_serializers_osgvolume.a */,
-				907D032115B86F8700575110 /* libosgdb_shp.a */,
-				907D032215B86F8700575110 /* libosgdb_stl.a */,
-				907D032315B86F8700575110 /* libosgdb_tga.a */,
-				907D032415B86F8700575110 /* libosgdb_tgz.a */,
-				907D032515B86F8700575110 /* libosgdb_trans.a */,
-				907D032615B86F8700575110 /* libosgdb_txf.a */,
-				907D032715B86F8700575110 /* libosgdb_txp.a */,
-				907D032815B86F8700575110 /* libosgdb_vtf.a */,
-				907D032915B86F8700575110 /* libosgdb_x.a */,
-				907D032A15B86F8700575110 /* libosgdb_zip.a */,
-				907D032B15B86F8700575110 /* libosgDB.a */,
-				907D032C15B86F8700575110 /* libosgFX.a */,
-				907D032D15B86F8700575110 /* libosgGA.a */,
-				907D032E15B86F8700575110 /* libosgManipulator.a */,
-				907D032F15B86F8700575110 /* libosgParticle.a */,
-				907D033015B86F8700575110 /* libosgPresentation.a */,
-				907D033115B86F8700575110 /* libosgShadow.a */,
-				907D033215B86F8700575110 /* libosgSim.a */,
-				907D033315B86F8700575110 /* libosgTerrain.a */,
-				907D033415B86F8700575110 /* libosgText.a */,
-				907D033515B86F8700575110 /* libosgUtil.a */,
-				907D033615B86F8700575110 /* libosgViewer.a */,
-				907D033715B86F8700575110 /* libosgVolume.a */,
-				907D033815B86F8700575110 /* libosgWidget.a */,
-			);
-			name = osg;
-			sourceTree = "<group>";
-		};
-		907D039615B86F8C00575110 /* osgEarth */ = {
-			isa = PBXGroup;
-			children = (
-				907D039715B86F9E00575110 /* libosgdb_kml.a */,
-				907D039815B86F9E00575110 /* libosgdb_osgearth_feature_wfs.a */,
-				907D039915B86F9E00575110 /* libosgdb_osgearth_feature_tfs.a */,
-				907D039A15B86F9E00575110 /* libosgdb_osgearth_tms.a */,
-				907D039B15B86F9E00575110 /* libosgdb_osgearth_wms.a */,
-				907D039C15B86F9E00575110 /* libosgdb_osgearth_label_overlay.a */,
-				907D039D15B86F9E00575110 /* libosgdb_osgearth_xyz.a */,
-				907D039E15B86F9E00575110 /* libosgEarthUtil.a */,
-				907D039F15B86F9E00575110 /* libosgdb_osgearth_label_annotation.a */,
-				907D03A015B86F9E00575110 /* libosgdb_osgearth_mask_feature.a */,
-				907D03A115B86F9E00575110 /* libosgdb_osgearth_model_feature_geom.a */,
-				907D03A215B86F9E00575110 /* libosgEarthAnnotation.a */,
-				907D03A315B86F9E00575110 /* libosgdb_osgearth_agglite.a */,
-				907D03A415B86F9E00575110 /* libosgdb_osgearth_feature_ogr.a */,
-				907D03A515B86F9E00575110 /* libosgdb_osgearth_model_feature_stencil.a */,
-				907D03A615B86F9E00575110 /* libosgdb_osgearth_vdatum_egm2008.a */,
-				907D03A715B86F9E00575110 /* libosgdb_osgearth_model_simple.a */,
-				907D03A815B86F9E00575110 /* libosgdb_osgearth_engine_osgterrain.a */,
-				90283E5C15C7091A00620EEF /* libosgdb_osgearth_engine_quadtree.a */,
-				907D03A915B86F9E00575110 /* libosgEarthFeatures.a */,
-				907D03AA15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm96.a */,
-				907D03AB15B86F9E00575110 /* libosgdb_osgearth_ocean_surface.a */,
-				907D03AC15B86F9E00575110 /* libosgdb_osgearth_debug.a */,
-				907D03AD15B86F9E00575110 /* libosgdb_osgearth_mbtiles.a */,
-				907D03AE15B86F9E00575110 /* libosgdb_osgearth_vdatum_egm84.a */,
-				907D03AF15B86F9E00575110 /* libosgdb_osgearth_tileservice.a */,
-				907D03B015B86F9E00575110 /* libosgdb_osgearth_yahoo.a */,
-				907D03B115B86F9E00575110 /* libosgdb_osgearth_arcgis_map_cache.a */,
-				907D03B215B86F9E00575110 /* libosgdb_osgearth_tilecache.a */,
-				907D03B315B86F9E00575110 /* libosgdb_osgearth_wcs.a */,
-				907D03B415B86F9E00575110 /* libosgEarthSymbology.a */,
-				907D03B515B86F9E00575110 /* libosgdb_osgearth_gdal.a */,
-				907D03B615B86F9E00575110 /* libosgdb_osgearth_refresh.a */,
-				907D03B715B86F9E00575110 /* libosgdb_osgearth_vpb.a */,
-				907D03B815B86F9E00575110 /* libosgdb_earth.a */,
-				907D03B915B86F9E00575110 /* libosgdb_osgearth_cache_filesystem.a */,
-				907D03BA15B86F9E00575110 /* libosgdb_osgearth_arcgis.a */,
-				907D03BB15B86F9E00575110 /* libosgdb_osgearth_osg.a */,
-				907D03BC15B86F9E00575110 /* libosgEarth.a */,
-			);
-			name = osgEarth;
-			sourceTree = "<group>";
-		};
-		907D0A9215B8DDAF00575110 /* ShaderGen */ = {
-			isa = PBXGroup;
-			children = (
-				907D0A8F15B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp */,
-				907D0A9015B8DDAA00575110 /* GLES2ShaderGenVisitor.h */,
-			);
-			name = ShaderGen;
-			sourceTree = "<group>";
-		};
-		908C793D15BB0B4E001CFA5E /* MultiTouchManipulator */ = {
-			isa = PBXGroup;
-			children = (
-				903B45D915C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp */,
-				903B45DA15C0DE9F00F7702B /* EarthMultiTouchManipulator.h */,
-			);
-			path = MultiTouchManipulator;
-			sourceTree = "<group>";
-		};
-		90A0DD7215B7BB68004FACEE /* gdal */ = {
-			isa = PBXGroup;
-			children = (
-				90A0DD7015B7BB64004FACEE /* libgdal.a */,
-			);
-			name = gdal;
-			path = "../../../../3rdParty/gdal-ios-device/lib";
-			sourceTree = "<group>";
-		};
-		90A0DD7315B7BB72004FACEE /* freetype */ = {
-			isa = PBXGroup;
-			children = (
-				90A0DD6C15B7BAF9004FACEE /* libFreeType_iphone_universal.a */,
-			);
-			name = freetype;
-			path = "../../../../3rdParty/freetype-ios-universal/lib";
-			sourceTree = "<group>";
-		};
-		90A0DD7415B7BB7C004FACEE /* proj4 */ = {
-			isa = PBXGroup;
-			children = (
-				90A0DD6E15B7BB50004FACEE /* libproj.a */,
-			);
-			name = proj4;
-			path = "../../../../3rdParty/proj4-ios-device/lib";
-			sourceTree = "<group>";
-		};
-		90A0DD7715B7BBAB004FACEE /* Libs */ = {
-			isa = PBXGroup;
-			children = (
-				907D0A8D15B8CEBE00575110 /* libGEOS_3.2.a */,
-				907D039615B86F8C00575110 /* osgEarth */,
-				907D02DB15B86F7100575110 /* osg */,
-				90A0DD7815B7BBCE004FACEE /* curl */,
-				90A0DD7415B7BB7C004FACEE /* proj4 */,
-				90A0DD7315B7BB72004FACEE /* freetype */,
-				90A0DD7215B7BB68004FACEE /* gdal */,
-				905100D015B2185000D9ABD3 /* libiconv.2.4.0.dylib */,
-				905100CE15B217B500D9ABD3 /* libz.1.1.3.dylib */,
-				905100CC15B217A800D9ABD3 /* libc++.dylib */,
-				907D03F915B8C31A00575110 /* libsqlite3.dylib */,
-				9051FFFD15B1EDFD00D9ABD3 /* Frameworks */,
-			);
-			name = Libs;
-			sourceTree = "<group>";
-		};
-		90A0DD7815B7BBCE004FACEE /* curl */ = {
-			isa = PBXGroup;
-			children = (
-				90A0DD7515B7BBA4004FACEE /* libcurl.a */,
-			);
-			name = curl;
-			path = "../../../../3rdParty/curl-ios-device/lib";
-			sourceTree = "<group>";
-		};
-		90B8676915C8895100F5CDC3 /* StartView */ = {
-			isa = PBXGroup;
-			children = (
-				90B8676315C8894900F5CDC3 /* StartViewerController.h */,
-				90B8676415C8894900F5CDC3 /* StartViewerController.m */,
-				90B8676515C8894900F5CDC3 /* StartViewerController.xib */,
-			);
-			name = StartView;
-			sourceTree = "<group>";
-		};
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
-		9051FFF915B1EDFD00D9ABD3 /* osgEarthViewerIOS */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = 9051002315B1EDFD00D9ABD3 /* Build configuration list for PBXNativeTarget "osgEarthViewerIOS" */;
-			buildPhases = (
-				9051FFF615B1EDFD00D9ABD3 /* Sources */,
-				9051FFF715B1EDFD00D9ABD3 /* Frameworks */,
-				9051FFF815B1EDFD00D9ABD3 /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = osgEarthViewerIOS;
-			productName = osgEarthViewerIOS;
-			productReference = 9051FFFA15B1EDFD00D9ABD3 /* osgEarth.app */;
-			productType = "com.apple.product-type.application";
-		};
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
-		9051FFF115B1EDFD00D9ABD3 /* Project object */ = {
-			isa = PBXProject;
-			attributes = {
-				LastUpgradeCheck = 0430;
-			};
-			buildConfigurationList = 9051FFF415B1EDFD00D9ABD3 /* Build configuration list for PBXProject "osgEarthViewerIOS" */;
-			compatibilityVersion = "Xcode 3.2";
-			developmentRegion = English;
-			hasScannedForEncodings = 0;
-			knownRegions = (
-				en,
-				English,
-			);
-			mainGroup = 9051FFEF15B1EDFD00D9ABD3;
-			productRefGroup = 9051FFFB15B1EDFD00D9ABD3 /* Products */;
-			projectDirPath = "";
-			projectRoot = "";
-			targets = (
-				9051FFF915B1EDFD00D9ABD3 /* osgEarthViewerIOS */,
-			);
-		};
-/* End PBXProject section */
-
-/* Begin PBXResourcesBuildPhase section */
-		9051FFF815B1EDFD00D9ABD3 /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				9051000D15B1EDFD00D9ABD3 /* InfoPlist.strings in Resources */,
-				9051001D15B1EDFD00D9ABD3 /* ViewController_iPhone.xib in Resources */,
-				9051002015B1EDFD00D9ABD3 /* ViewController_iPad.xib in Resources */,
-				90283E5615C6FB3E00620EEF /* tests in Resources */,
-				90283E5715C6FB3E00620EEF /* data in Resources */,
-				90B8676715C8894900F5CDC3 /* StartViewerController.xib in Resources */,
-				90DABDDC15CEFF9700D0F609 /* moon_1024x512.jpg in Resources */,
-				1FA5C122177B608000A161FF /* gdal_data in Resources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXResourcesBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
-		9051FFF615B1EDFD00D9ABD3 /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				9051000F15B1EDFD00D9ABD3 /* main.m in Sources */,
-				9051001315B1EDFD00D9ABD3 /* AppDelegate.m in Sources */,
-				9051001A15B1EDFD00D9ABD3 /* ViewController.m in Sources */,
-				907D0A9115B8DDAA00575110 /* GLES2ShaderGenVisitor.cpp in Sources */,
-				903B45DB15C0DE9F00F7702B /* EarthMultiTouchManipulator.cpp in Sources */,
-				90B8676615C8894900F5CDC3 /* StartViewerController.m in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXSourcesBuildPhase section */
-
-/* Begin PBXVariantGroup section */
-		9051000B15B1EDFD00D9ABD3 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				9051000C15B1EDFD00D9ABD3 /* en */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
-		9051001B15B1EDFD00D9ABD3 /* ViewController_iPhone.xib */ = {
-			isa = PBXVariantGroup;
-			children = (
-				9051001C15B1EDFD00D9ABD3 /* en */,
-			);
-			name = ViewController_iPhone.xib;
-			sourceTree = "<group>";
-		};
-		9051001E15B1EDFD00D9ABD3 /* ViewController_iPad.xib */ = {
-			isa = PBXVariantGroup;
-			children = (
-				9051001F15B1EDFD00D9ABD3 /* en */,
-			);
-			name = ViewController_iPad.xib;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
-/* Begin XCBuildConfiguration section */
-		9051002115B1EDFD00D9ABD3 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				COPY_PHASE_STRIP = NO;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_DYNAMIC_NO_PIC = NO;
-				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					"DEBUG=1",
-					"$(inherited)",
-				);
-				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
-				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
-				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
-			};
-			name = Debug;
-		};
-		9051002215B1EDFD00D9ABD3 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				ALWAYS_SEARCH_USER_PATHS = NO;
-				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
-				COPY_PHASE_STRIP = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu99;
-				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
-				GCC_WARN_ABOUT_RETURN_TYPE = YES;
-				GCC_WARN_UNINITIALIZED_AUTOS = YES;
-				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
-				OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
-				SDKROOT = iphoneos;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VALIDATE_PRODUCT = YES;
-			};
-			name = Release;
-		};
-		9051002415B1EDFD00D9ABD3 /* Debug */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Jefferey Smith (KN8HX7RLT9)";
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch";
-				"GCC_THUMB_SUPPORT[arch=armv6]" = "";
-				HEADER_SEARCH_PATHS = (
-					../../,
-					../../../../osg/OpenSceneGraph/include,
-				);
-				INFOPLIST_FILE = "osgEarthViewerIOS/osgEarthViewerIOS-Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
-				LIBRARY_SEARCH_PATHS = (
-					"\"$(SRCROOT)/../../../../3rdParty/all\"",
-					"\"$(SRCROOT)/../../../lib/Release\"",
-					"\"$(SRCROOT)/../../../../osg/OpenSceneGraph/lib\"",
-				);
-				PRODUCT_NAME = osgEarth;
-				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "B0AE9D26-0AD5-4EE2-ADC5-8559501E74A9";
-				VALID_ARCHS = armv7;
-				WRAPPER_EXTENSION = app;
-			};
-			name = Debug;
-		};
-		9051002515B1EDFD00D9ABD3 /* Release */ = {
-			isa = XCBuildConfiguration;
-			buildSettings = {
-				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Pelican Ventures, Inc";
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
-				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREFIX_HEADER = "osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch";
-				"GCC_THUMB_SUPPORT[arch=armv6]" = "";
-				HEADER_SEARCH_PATHS = (
-					../../,
-					../../../../osg/OpenSceneGraph/include,
-				);
-				INFOPLIST_FILE = "osgEarthViewerIOS/osgEarthViewerIOS-Info.plist";
-				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
-				LIBRARY_SEARCH_PATHS = (
-					"\"$(SRCROOT)/../../../../3rdParty/all\"",
-					"\"$(SRCROOT)/../../../lib/Release\"",
-					"\"$(SRCROOT)/../../../../osg/OpenSceneGraph/lib\"",
-				);
-				PRODUCT_NAME = osgEarth;
-				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "3948EA51-CDCD-4B29-A73F-67C55D186433";
-				VALID_ARCHS = armv7;
-				WRAPPER_EXTENSION = app;
-			};
-			name = Release;
-		};
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
-		9051002315B1EDFD00D9ABD3 /* Build configuration list for PBXNativeTarget "osgEarthViewerIOS" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				9051002415B1EDFD00D9ABD3 /* Debug */,
-				9051002515B1EDFD00D9ABD3 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-		9051FFF415B1EDFD00D9ABD3 /* Build configuration list for PBXProject "osgEarthViewerIOS" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				9051002115B1EDFD00D9ABD3 /* Debug */,
-				9051002215B1EDFD00D9ABD3 /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
-/* End XCConfigurationList section */
-	};
-	rootObject = 9051FFF115B1EDFD00D9ABD3 /* Project object */;
-}
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 0901c6b..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Workspace
-   version = "1.0">
-   <FileRef
-      location = "self:osgEarthViewerIOS.xcodeproj">
-   </FileRef>
-</Workspace>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.cpp b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.cpp
deleted file mode 100755
index a2043f1..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.cpp
+++ /dev/null
@@ -1,581 +0,0 @@
-/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield 
- *
- * This library is open source and may be redistributed and/or modified under  
- * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
- * (at your option) any later version.  The full license is in LICENSE file
- * included with this distribution, and on the openscenegraph.org website.
- * 
- * 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 
- * OpenSceneGraph Public License for more details.
- */
-
-/**
- * \brief    Shader generator framework.
- * \author   Maciej Krol
- */
-
-#include "GLES2ShaderGenVisitor.h"
-#include <osg/Geode>
-#include <osg/Geometry> // for ShaderGenVisitor::update
-#include <osg/Fog>
-#include <osg/Material>
-#include <sstream>
-
-#ifndef WIN32
-#define SHADER_COMPAT \
-"#ifndef GL_ES\n" \
-"#if (__VERSION__ <= 110)\n" \
-"#define lowp\n" \
-"#define mediump\n" \
-"#define highp\n" \
-"#endif\n" \
-"#endif\n"
-#else
-#define SHADER_COMPAT ""
-#endif
-
-
-using namespace osgUtil;
-
-namespace osgUtil
-{
-    
-    /// State extended by mode/attribute accessors
-    class StateEx : public osg::State
-    {
-    public:
-        StateEx() : State() {}
-        
-        osg::StateAttribute::GLModeValue getMode(osg::StateAttribute::GLMode mode,
-                                                 osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
-        {
-            return getMode(_modeMap, mode, def);
-        }
-        
-        osg::StateAttribute *getAttribute(osg::StateAttribute::Type type, unsigned int member = 0) const
-        {
-            return getAttribute(_attributeMap, type, member);
-        }
-        
-        osg::StateAttribute::GLModeValue getTextureMode(unsigned int unit,
-                                                        osg::StateAttribute::GLMode mode,
-                                                        osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
-        {
-            return unit < _textureModeMapList.size() ? getMode(_textureModeMapList[unit], mode, def) : def;
-        }
-        
-        osg::StateAttribute *getTextureAttribute(unsigned int unit, osg::StateAttribute::Type type) const
-        {
-            return unit < _textureAttributeMapList.size() ? getAttribute(_textureAttributeMapList[unit], type, 0) : 0;
-        }
-        
-        osg::Uniform *getUniform(const std::string& name) const
-        {
-            UniformMap::const_iterator it = _uniformMap.find(name);
-            return it != _uniformMap.end() ? 
-            const_cast<osg::Uniform *>(it->second.uniformVec.back().first) : 0;
-        }
-        
-    protected:
-        
-        osg::StateAttribute::GLModeValue getMode(const ModeMap &modeMap,
-                                                 osg::StateAttribute::GLMode mode, 
-                                                 osg::StateAttribute::GLModeValue def = osg::StateAttribute::INHERIT) const
-        {
-            ModeMap::const_iterator it = modeMap.find(mode);
-            return (it != modeMap.end() && it->second.valueVec.size()) ? it->second.valueVec.back() : def;
-        }
-        
-        osg::StateAttribute *getAttribute(const AttributeMap &attributeMap,
-                                          osg::StateAttribute::Type type, unsigned int member = 0) const
-        {
-            AttributeMap::const_iterator it = attributeMap.find(std::make_pair(type, member));
-            return (it != attributeMap.end() && it->second.attributeVec.size()) ? 
-            const_cast<osg::StateAttribute*>(it->second.attributeVec.back().first) : 0;
-        }
-    };
-    
-}
-
-void GLES2ShaderGenCache::setStateSet(int stateMask, osg::StateSet *stateSet)
-{
-    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-    _stateSetMap[stateMask] = stateSet;
-}
-
-osg::StateSet* GLES2ShaderGenCache::getStateSet(int stateMask) const
-{
-    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-    StateSetMap::const_iterator it = _stateSetMap.find(stateMask);
-    return (it != _stateSetMap.end()) ? it->second.get() : 0;
-}
-
-osg::StateSet* GLES2ShaderGenCache::getOrCreateStateSet(int stateMask)
-{
-    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-    StateSetMap::iterator it = _stateSetMap.find(stateMask);
-    if (it == _stateSetMap.end())
-    {
-        osg::StateSet *stateSet = createStateSet(stateMask);
-        _stateSetMap.insert(it, StateSetMap::value_type(stateMask, stateSet));
-        return stateSet;
-    }
-    return it->second.get();
-}
-
-osg::StateSet* GLES2ShaderGenCache::createStateSet(int stateMask) const
-{
-    osg::StateSet *stateSet = new osg::StateSet;
-    osg::Program *program = new osg::Program;
-    stateSet->setAttribute(program);
-    
-    std::ostringstream vert;
-    std::ostringstream frag;
-    
-    //first add the shader compat defines so that we don't have issues with using precision stuff
-    vert << SHADER_COMPAT;
-    frag << SHADER_COMPAT;
-    
-    // write varyings
-    if ((stateMask & LIGHTING) && !(stateMask & NORMAL_MAP))
-    {
-        vert << "varying highp vec3 normalDir;\n";
-    }
-    
-    if (stateMask & (LIGHTING | NORMAL_MAP))
-    {
-        vert << "struct osg_LightSourceParameters {"
-        << "    mediump vec4  ambient;"
-        << "    mediump vec4  diffuse;"
-        << "    mediump vec4  specular;"
-        << "    mediump vec4  position;"
-        << "    mediump vec4  halfVector;"
-        << "    mediump vec3  spotDirection;" 
-        << "    mediump float  spotExponent;"
-        << "    mediump float  spotCutoff;"
-        << "    mediump float  spotCosCutoff;" 
-        << "    mediump float  constantAttenuation;"
-        << "    mediump float  linearAttenuation;"
-        << "    mediump float  quadraticAttenuation;" 
-        << "};\n"
-        << "uniform osg_LightSourceParameters osg_LightSource[" << 1 << "];\n"
-        
-        << "struct  osg_LightProducts {"
-        << "    mediump vec4  ambient;"
-        << "    mediump vec4  diffuse;"
-        << "    mediump vec4  specular;"
-        << "};\n"
-        << "uniform osg_LightProducts osg_FrontLightProduct[" << 1 << "];\n"
-        
-        << "varying highp vec3 lightDir;\n";
-    }
-    
-    if (stateMask & (LIGHTING | NORMAL_MAP | FOG))
-    {
-        vert << "varying highp vec3 viewDir;\n";
-    }
-    
-    //add texcoord varying if using gles2 as built in gl_TexCoord does not exist,
-    //also no gl_FrontColor so we will define vColor
-#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE
-    if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
-    {
-        vert << "varying mediump vec4 texCoord0;\n";
-    }
-    vert << "varying mediump vec4 vColor;\n";
-#endif
-    
-    // copy varying to fragment shader
-    frag << vert.str();
-    
-    //add our material replacment uniforms for non fixed function versions
-#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE
-    if (stateMask & (LIGHTING | NORMAL_MAP))
-    {
-        frag <<
-        "struct osgMaterial{\n"\
-        "  mediump vec4 ambient;\n"\
-        "  mediump vec4 diffuse;\n"\
-        "  mediump vec4 specular;\n"\
-        "  mediump float shine;\n"\
-        "};\n"\
-        
-        "uniform osgMaterial osg_Material;\n";
-    }
-#endif
-    
-
-    // write uniforms and attributes
-    int unit = 0;
-    
-    //add the replacements for gl matricies and verticies
-#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE
-    //vert << "attribute vec4 gl_Vertex;\n";
-    vert << "attribute vec4 osg_Color;\n";
-    vert << "uniform mat4 osg_ModelViewProjectionMatrix;\n";
-#endif
-    
-    if (stateMask & DIFFUSE_MAP)
-    {
-        osg::Uniform *diffuseMap = new osg::Uniform("diffuseMap", unit++);
-        stateSet->addUniform(diffuseMap);
-        frag << "uniform sampler2D diffuseMap;\n";
-    }
-    
-    if (stateMask & NORMAL_MAP)
-    {
-        osg::Uniform *normalMap = new osg::Uniform("normalMap", unit++);
-        stateSet->addUniform(normalMap);
-        frag << "uniform sampler2D normalMap;\n";
-        program->addBindAttribLocation("tangent", 6);
-        vert << "attribute vec3 tangent;\n";
-    }
-    
-    
-#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE
-
-    //non fixed function texturing
-    if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
-    {
-        vert << "attribute vec4 osg_MultiTexCoord0;\n";
-    }
-    
-    //non fixed function normal info
-    if (stateMask & (LIGHTING | NORMAL_MAP))
-    {
-        //vert << "uniform mat4 osg_ModelViewMatrix;\n";
-        //vert << "attribute vec3 osg_Normal;\n";
-        //vert << "uniform mat3 osg_NormalMatrix;\n";        
-    }
-#endif
-    
-    vert << "\n"\
-    "void main()\n"\
-    "{\n"\
-    
-    //ftransform does not exist in gles2 (need to see if it's still in GL3)
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-    "  gl_Position = ftransform();\n";
-#else
-    "  gl_Position = osg_ModelViewProjectionMatrix * gl_Vertex;\n";
-#endif
-    
-    if (stateMask & (DIFFUSE_MAP | NORMAL_MAP))
-    {
-        //gles2 does not have built in gl_TexCoord varying
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        vert << "  gl_TexCoord[0] = gl_MultiTexCoord0;\n";
-#else
-        vert << "  texCoord0 = osg_MultiTexCoord0;\n";
-#endif
-    }
-    
-    //
-    
-    if (stateMask & NORMAL_MAP)
-    {
-        //gl_NormalMatrix etc should be replaced automatically
-        vert << 
-        "  highp vec3 n = gl_NormalMatrix * gl_Normal;\n"\
-        "  highp vec3 t = gl_NormalMatrix * tangent;\n"\
-        "  highp vec3 b = cross(n, t);\n"\
-        "  highp vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
-        "  viewDir.x = dot(dir, t);\n"\
-        "  viewDir.y = dot(dir, b);\n"\
-        "  viewDir.z = dot(dir, n);\n";
-//use fixed light pos for now where gl_LightSource is not avaliable
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE 
-        vert << "  vec4 lpos = gl_LightSource[0].position;\n";
-#else
-        vert << "  highp vec4 lpos = osg_LightSource[0].position;\n";
-#endif
-        vert << 
-        "  if (lpos.w == 0.0)\n"\
-        "    dir = lpos.xyz;\n"\
-        "  else\n"\
-        "    dir += lpos.xyz;\n"\
-        "  lightDir.x = dot(dir, t);\n"\
-        "  lightDir.y = dot(dir, b);\n"\
-        "  lightDir.z = dot(dir, n);\n";
-    }
-    else if (stateMask & LIGHTING)
-    {
-        vert << 
-        "  normalDir = gl_NormalMatrix * gl_Normal;\n"\
-        "  highp vec3 dir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n"\
-        "  viewDir = dir;\n";
-//use fixed light pos for now where gl_LightSource is not avaliable
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        vert << "  vec4 lpos = gl_LightSource[0].position;\n";
-#else
-        vert << "  vec4 lpos = osg_LightSource[0].position;\n";
-#endif
-        vert <<
-        "  if (lpos.w == 0.0)\n"\
-        "    lightDir = lpos.xyz;\n"\
-        "  else\n"\
-        "    lightDir = lpos.xyz + dir;\n";
-    }
-    else if (stateMask & FOG)
-    {
-        vert << "  viewDir = -vec3(gl_ModelViewMatrix * gl_Vertex);\n";
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        vert << "  gl_FrontColor = gl_Color;\n";
-#else
-        vert << "  vColor = osg_Color;\n";
-#endif
-    }
-    else
-    {
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        vert << "  gl_FrontColor = gl_Color;\n";
-#else
-        vert << "  vColor = osg_Color;\n";
-#endif
-    }
-    
-    vert << "}\n";
-    
-    frag << "\n"\
-    "void main()\n"\
-    "{\n";
-    
-    if (stateMask & DIFFUSE_MAP)
-    {
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        frag << "  vec4 base = texture2D(diffuseMap, gl_TexCoord[0].st);\n";
-#else
-        frag << "  mediump vec4 base = texture2D(diffuseMap, texCoord0.st);\n";
-#endif
-    }
-    else
-    {
-        frag << "  mediump vec4 base = vec4(1.0);\n";
-    }
-    
-    if (stateMask & NORMAL_MAP)
-    {
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        frag << "  vec3 normalDir = texture2D(normalMap, gl_TexCoord[0].st).xyz*2.0-1.0;\n";
-#else
-        frag << "  mediump vec3 normalDir = texture2D(normalMap, texCoord0.st).xyz*2.0-1.0;\n";        
-        //frag << " normalDir.g = -normalDir.g;\n";
-#endif
-    }
-    
-    if (stateMask & (LIGHTING | NORMAL_MAP))
-    {
-//for now we will just have two versions of the below, once we have access to lights
-//then we can completely replace use of gl_FrontLightModelProduct
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        frag << 
-        "  highp vec3 nd = normalize(normalDir);\n"\
-        "  highp vec3 ld = normalize(lightDir);\n"\
-        "  highp vec3 vd = normalize(viewDir);\n"\
-        "  mediump vec4 color = gl_FrontLightModelProduct.sceneColor;\n"\
-        "  color += gl_FrontLightProduct[0].ambient;\n"\
-        "  mediump float diff = max(dot(ld, nd), 0.0);\n"\
-        "  color += gl_FrontLightProduct[0].diffuse * diff;\n"\
-        "  color *= base;\n"\
-        "  if (diff > 0.0)\n"\
-        "  {\n"\
-        "    highp vec3 halfDir = normalize(ld+vd);\n"\
-        "    color.rgb += base.a * gl_FrontLightProduct[0].specular.rgb * \n"\
-        "      pow(max(dot(halfDir, nd), 0.0), gl_FrontMaterial.shininess);\n"\
-        "  }\n";
-#else
-        frag << 
-        "  highp vec3 nd = normalize(normalDir);\n"\
-        "  highp vec3 ld = normalize(lightDir);\n"\
-        "  highp vec3 vd = normalize(viewDir);\n"\
-        "  mediump vec4 color = vec4(0.01,0.01,0.01,1.0);\n"\
-        "  color += osg_FrontLightProduct[0].ambient;\n"\
-        "  mediump float diff = max(dot(ld, nd), 0.0);\n"\
-        "  color += osg_FrontLightProduct[0].diffuse * diff;\n"\
-        "  color *= base;\n"\
-        "  if (diff > 0.0)\n"\
-        "  {\n"\
-        "    highp vec3 halfDir = normalize(ld+vd);\n"\
-        "    color.rgb += base.a * osg_FrontLightProduct[0].specular.rgb * \n"\
-        "      pow(max(dot(halfDir, nd), 0.0), osg_Material.shine);\n"\
-        "  }\n";       
-#endif
-    }
-    else
-    {
-        frag << "  mediump vec4 color = base;\n";
-    }
-    
-    if (!(stateMask & LIGHTING))
-    {
-#ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
-        frag << "  color *= gl_Color;\n";
-#else
-        frag << "  color *= vColor;\n";
-#endif
-    }
-    
-    if (stateMask & FOG)
-    {
-        frag << 
-        "  float d2 = dot(viewDir, viewDir);//gl_FragCoord.z/gl_FragCoord.w;\n"\
-        "  float f = exp2(-1.442695*gl_Fog.density*gl_Fog.density*d2);\n"\
-        "  color.rgb = mix(gl_Fog.color.rgb, color.rgb, clamp(f, 0.0, 1.0));\n";
-    }
-    
-    frag << "  gl_FragColor = color;\n";
-    frag << "}\n";
-    
-    std::string vertstr = vert.str();
-    std::string fragstr = frag.str();
-    
-    OSG_DEBUG << "ShaderGenCache Vertex shader:\n" << vertstr << std::endl;
-    OSG_DEBUG << "ShaderGenCache Fragment shader:\n" << fragstr << std::endl;
-    
-    program->addShader(new osg::Shader(osg::Shader::VERTEX, vertstr));
-    program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragstr));
-    
-    return stateSet;
-}
-
-GLES2ShaderGenVisitor::GLES2ShaderGenVisitor() : 
-NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-_stateCache(new GLES2ShaderGenCache),
-_state(new StateEx)
-{
-}
-
-GLES2ShaderGenVisitor::GLES2ShaderGenVisitor(GLES2ShaderGenCache *stateCache) : 
-NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-_stateCache(stateCache),
-_state(new StateEx)
-{
-}
-
-void GLES2ShaderGenVisitor::setRootStateSet(osg::StateSet *stateSet)
-{
-    if (_rootStateSet.valid())
-        _state->removeStateSet(0);
-    _rootStateSet = stateSet;
-    if (_rootStateSet.valid())
-        _state->pushStateSet(_rootStateSet.get());
-}
-
-void GLES2ShaderGenVisitor::reset()
-{
-    _state->popAllStateSets();
-    if (_rootStateSet.valid())
-        _state->pushStateSet(_rootStateSet.get());
-}
-
-void GLES2ShaderGenVisitor::apply(osg::Node &node)
-{
-    osg::StateSet *stateSet = node.getStateSet();
-    
-    if (stateSet)
-        _state->pushStateSet(stateSet);
-    
-    traverse(node);
-    
-    if (stateSet)
-        _state->popStateSet();
-}
-
-void GLES2ShaderGenVisitor::apply(osg::Geode &geode)
-{
-    osg::StateSet *stateSet = geode.getStateSet();
-    if (stateSet)
-        _state->pushStateSet(stateSet);
-    
-    for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
-    {
-        osg::Drawable *drawable = geode.getDrawable(i);
-        osg::StateSet *ss = drawable->getStateSet();
-        if (ss)
-            _state->pushStateSet(ss);
-        
-        update(drawable);
-        
-        if (ss)
-            _state->popStateSet();
-    }
-    
-    if (stateSet)
-        _state->popStateSet();
-}
-
-void GLES2ShaderGenVisitor::update(osg::Drawable *drawable)
-{
-    // update only geometry due to compatibility issues with user defined drawables
-    osg::Geometry *geometry = drawable->asGeometry();
-#if 1
-    if (!geometry)
-        return;
-#endif
-    
-    StateEx *state = static_cast<StateEx *>(_state.get());
-    // skip nodes without state sets
-    if (state->getStateSetStackSize() == (_rootStateSet.valid() ? 1u : 0u))
-        return;
-    
-    // skip state sets with already attached programs
-    if (state->getAttribute(osg::StateAttribute::PROGRAM))
-        return;
-    
-    int stateMask = 0;
-    //if (state->getMode(GL_BLEND) & osg::StateAttribute::ON)
-    //    stateMask |= ShaderGen::BLEND;
-    if (state->getMode(GL_LIGHTING) & osg::StateAttribute::ON)
-        stateMask |= GLES2ShaderGenCache::LIGHTING;
-    if (state->getMode(GL_FOG) & osg::StateAttribute::ON)
-        stateMask |= GLES2ShaderGenCache::FOG;
-    if (state->getTextureAttribute(0, osg::StateAttribute::TEXTURE))
-        stateMask |= GLES2ShaderGenCache::DIFFUSE_MAP;
-    
-    if (state->getTextureAttribute(1, osg::StateAttribute::TEXTURE) && geometry!=0 &&
-        geometry->getVertexAttribArray(6)) //tangent
-        stateMask |= GLES2ShaderGenCache::NORMAL_MAP;
-    
-    // Get program and uniforms for accumulated state.
-    osg::StateSet *progss = _stateCache->getOrCreateStateSet(stateMask);
-    // Set program and uniforms to the last state set.
-    osg::StateSet *ss = const_cast<osg::StateSet *>(state->getStateSetStack().back());
-    ss->setAttribute(progss->getAttribute(osg::StateAttribute::PROGRAM));
-    ss->setUniformList(progss->getUniformList());
-    
-    //Edit, for now we will pinch the Material colors and bind as uniforms for non fixed function to replace gl_Front Material
-#ifndef OSG_GL_FIXED_FUNCTION_AVAILABLE
-    osg::Material* mat = dynamic_cast<osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
-    if(mat){
-        ss->addUniform(new osg::Uniform("osg_Material.ambient", mat->getAmbient(osg::Material::FRONT)));
-        ss->addUniform(new osg::Uniform("osg_Material.diffuse", mat->getDiffuse(osg::Material::FRONT)));
-        ss->addUniform(new osg::Uniform("osg_Material.specular", mat->getSpecular(osg::Material::FRONT)));
-        ss->addUniform(new osg::Uniform("osg_Material.shine", mat->getShininess(osg::Material::FRONT)));
-        ss->removeAttribute(osg::StateAttribute::MATERIAL);
-    }else{
-        //if no material then setup some reasonable defaults
-        ss->addUniform(new osg::Uniform("osg_Material.ambient", osg::Vec4(0.2f,0.2f,0.2f,1.0f)));
-        ss->addUniform(new osg::Uniform("osg_Material.diffuse", osg::Vec4(0.8f,0.8f,0.8f,1.0f)));
-        ss->addUniform(new osg::Uniform("osg_Material.specular", osg::Vec4(1.0f,1.0f,1.0f,1.0f)));
-        ss->addUniform(new osg::Uniform("osg_Material.shine", 16.0f));
-    }
-#endif
-    
-    
-    // remove any modes that won't be appropriate when using shaders
-    if ((stateMask & GLES2ShaderGenCache::LIGHTING)!=0)
-    {
-        ss->removeMode(GL_LIGHTING);
-        ss->removeMode(GL_LIGHT0);
-    }
-    if ((stateMask & GLES2ShaderGenCache::FOG)!=0)
-    {
-        ss->removeMode(GL_FOG);
-    }
-    if ((stateMask & GLES2ShaderGenCache::DIFFUSE_MAP)!=0) ss->removeTextureMode(0, GL_TEXTURE_2D);
-    if ((stateMask & GLES2ShaderGenCache::NORMAL_MAP)!=0) ss->removeTextureMode(1, GL_TEXTURE_2D);
-}
-
-
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.h b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.h
deleted file mode 100755
index ace8e3a..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/GLES2ShaderGenVisitor.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield 
- *
- * This library is open source and may be redistributed and/or modified under  
- * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
- * (at your option) any later version.  The full license is in LICENSE file
- * included with this distribution, and on the openscenegraph.org website.
- * 
- * 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 
- * OpenSceneGraph Public License for more details.
- */
-
-/**
- * \brief    Shader generator framework.
- * \author   Maciej Krol
- */
-
-#ifndef OSGUTIL_GLES2SHADER_STATE_
-#define OSGUTIL_GLES2SHADER_STATE_ 1
-
-#include <osgUtil/Export>
-#include <osg/NodeVisitor>
-#include <osg/State>
-
-namespace osgUtil
-{
-    
-    class GLES2ShaderGenCache : public osg::Referenced
-    {
-    public:
-        enum StateMask
-        {
-            BLEND = 1,
-            LIGHTING = 2,
-            FOG = 4,
-            DIFFUSE_MAP = 8, //< Texture in unit 0
-            NORMAL_MAP = 16  //< Texture in unit 1 and vertex attribute array 6
-        };
-        
-        typedef std::map<int, osg::ref_ptr<osg::StateSet> > StateSetMap;
-        
-        GLES2ShaderGenCache() {};
-        
-        void setStateSet(int stateMask, osg::StateSet *program);
-        osg::StateSet *getStateSet(int stateMask) const;
-        osg::StateSet *getOrCreateStateSet(int stateMask);
-        
-    protected:
-        osg::StateSet *createStateSet(int stateMask) const;
-        mutable OpenThreads::Mutex _mutex;
-        StateSetMap _stateSetMap;
-        
-    };
-    
-    class OSGUTIL_EXPORT GLES2ShaderGenVisitor : public osg::NodeVisitor
-    {
-    public:
-        GLES2ShaderGenVisitor();
-        GLES2ShaderGenVisitor(GLES2ShaderGenCache *stateCache);
-        
-        void setStateCache(GLES2ShaderGenCache *stateCache) { _stateCache = stateCache; }
-        GLES2ShaderGenCache *getStateCache() const { return _stateCache.get(); }
-        
-        /// Top level state set applied as the first one.
-        void setRootStateSet(osg::StateSet *stateSet);
-        osg::StateSet *getRootStateSet() const { return _rootStateSet.get(); }
-        
-        void apply(osg::Node &node);
-        void apply(osg::Geode &geode);
-        
-        void reset();
-        
-    protected:
-        void update(osg::Drawable *drawable);
-        
-        osg::ref_ptr<GLES2ShaderGenCache> _stateCache;
-        osg::ref_ptr<osg::State> _state;
-        osg::ref_ptr<osg::StateSet> _rootStateSet;
-    };
-    
-}
-
-#endif
\ No newline at end of file
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/ShaderGenScene.h b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/ShaderGenScene.h
deleted file mode 100755
index 527f6bf..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/ShaderGen/ShaderGenScene.h
+++ /dev/null
@@ -1,227 +0,0 @@
-#pragma once
-
-#include <osg/ShapeDrawable>
-#include <osg/Texture2D>
-#include <osg/MatrixTransform>
-#include <osgDB/ReadFile>
-#include <osgDB/FileUtils>
-#include <osgUtil/TangentSpaceGenerator>
-
-#include "GLES2ShaderGenVisitor.h"
-
-class GenerateTangentsVisitor : public osg::NodeVisitor
-{
-public:
-    std::vector<osg::Geode*> _geodesList;
-    GenerateTangentsVisitor()
-    : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
-    {
-    }
-    virtual void apply(osg::Geode& geode)
-    {
-        _geodesList.push_back(&geode);
-        //loop the geoms
-        for(unsigned int i=0; i<geode.getNumDrawables(); i++)
-        {
-            //cast drawable to geometry
-            osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
-            
-            if(geom)
-            {
-                //check if the geom already has the vectors
-                if( (geom->getVertexAttribArray(6) == 0) && (geom->getVertexAttribArray(7) == 0) )
-                {
-                    
-                    osgUtil::TangentSpaceGenerator* tangGen = new osgUtil::TangentSpaceGenerator();
-                    tangGen->generate(geom, 0);
-                    
-                    if(tangGen)
-                    {
-                        osg::Vec4Array* tangentArray = tangGen->getTangentArray(); 
-                        osg::Vec4Array* biNormalArray = tangGen->getBinormalArray();
-                        
-                        int size = tangentArray->size();
-                        int sizeb = biNormalArray->size();
-                        
-                        if( (size>0) && (sizeb>0))
-                        {
-                            geom->setVertexAttribArray(6, tangentArray);
-                            geom->setVertexAttribBinding(6, osg::Geometry::BIND_PER_VERTEX);  
-                            
-                            //geom->setVertexAttribArray(7, biNormalArray);
-                            //geom->setVertexAttribBinding(7, osg::Geometry::BIND_PER_VERTEX);  
-                        }
-                    }
-                }
-            }
-        }
-        traverse(geode);
-    }
-protected:
-    
-};
-
-class ShaderGenScene
-{
-public:
-    ShaderGenScene(){
-        
-    }
-    virtual ~ShaderGenScene(){
-    }
-    
-    static osg::MatrixTransform* CreateScene(){
-        osg::MatrixTransform* root = new osg::MatrixTransform();
-        //move whole scene in front of camera
-        root->setMatrix(osg::Matrix::translate(osg::Vec3(0.0f,0.0f,-100.0f)));
-
-        //create each type with an offset
-        float size = 20.0f;
-        float offset = size+1.0f;
-        
-        //top row
-        root->addChild(ShaderGenScene::CreateColoredShape(osg::Vec3(-offset,offset,0.0f), size));
-        
-        root->addChild(ShaderGenScene::CreateColoredLitShape(osg::Vec3(0.0f,offset,0.0f), size));
-        
-        root->addChild(ShaderGenScene::CreateTexturedShape(osg::Vec3(offset,offset,0.0f), size));
-        
-        //bottom row
-        root->addChild(ShaderGenScene::CreateTexturedLitShape(osg::Vec3(-offset,-offset,0.0f), size));
-        
-        root->addChild(ShaderGenScene::CreateColoredNormalMappedShape(osg::Vec3(0.0f,-offset,0.0f), size));
-        
-        root->addChild(ShaderGenScene::CreateTexturedNormalMappedShape(osg::Vec3(offset,-offset,0.0f), size));
-        
-        //apply shader gen to entire root
-        //osgUtil::GLES2ShaderGenVisitor shaderGen;
-        //root->accept(shaderGen);
-        
-        return root;
-    }
-    
-    static osg::MatrixTransform* CreateColoredShape(osg::Vec3 offset, float size){
-        osg::Sphere* sphere = new osg::Sphere();
-        sphere->setRadius(size*0.5f);
-        osg::ShapeDrawable* shape = new osg::ShapeDrawable(sphere);
-        osg::Geode* geode = new osg::Geode();
-        geode->addDrawable(shape);
-        
-        osg::StateSet* state = geode->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::translate(offset));
-        transform->addChild(geode);
-        return transform;
-    }
-    
-    static osg::MatrixTransform* CreateColoredLitShape(osg::Vec3 offset, float size){
-        osg::Sphere* sphere = new osg::Sphere();
-        sphere->setRadius(size*0.5f);
-        osg::ShapeDrawable* shape = new osg::ShapeDrawable(sphere);
-        osg::Geode* geode = new osg::Geode();
-        geode->addDrawable(shape);
-        
-        osg::StateSet* state = geode->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::ON);
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::translate(offset));
-        transform->addChild(geode);
-        return transform;
-    }
-    
-    static osg::MatrixTransform* CreateTexturedShape(osg::Vec3 offset, float size){
-        osg::Sphere* sphere = new osg::Sphere();
-        sphere->setRadius(size*0.5f);
-        osg::ShapeDrawable* shape = new osg::ShapeDrawable(sphere);
-        osg::Geode* geode = new osg::Geode();
-        geode->addDrawable(shape);
-        
-        osg::StateSet* state = geode->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-        state->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/diffuse.png"))), osg::StateAttribute::ON);
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::translate(offset));
-        transform->addChild(geode);
-        return transform;
-    } 
-    
-    static osg::MatrixTransform* CreateTexturedLitShape(osg::Vec3 offset, float size){
-        osg::Sphere* sphere = new osg::Sphere();
-        sphere->setRadius(size*0.5f);
-        osg::ShapeDrawable* shape = new osg::ShapeDrawable(sphere);
-        osg::Geode* geode = new osg::Geode();
-        geode->addDrawable(shape);
-        
-        osg::StateSet* state = geode->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::ON);
-        state->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/diffuse.png"))), osg::StateAttribute::ON);
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::translate(offset));
-        transform->addChild(geode);
-        return transform;
-    } 
-
-    static osg::MatrixTransform* CreateColoredNormalMappedShape(osg::Vec3 offset, float size){
-        osg::Node* model = osgDB::readNodeFile(osgDB::findDataFile("Models/sphere.osg"));
-        if(!model){
-            OSG_FATAL << "ERROR: Failed to load model 'Models/sphere.osg' no normal mapping example avaliable." << std::endl;
-            return false;
-        }
-        
-        //generate tangent vectors
-        GenerateTangentsVisitor genTangents;
-        model->accept(genTangents);
-        
-        osg::StateSet* state = model->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::ON);
-        //state->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/diffuse.png"))), osg::StateAttribute::ON);
-        state->setTextureAttributeAndModes(1, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/normal.png"))), osg::StateAttribute::ON);
-        
-        //make sure the model fits the scene scale
-        float radius = model->computeBound().radius();
-        OSG_FATAL << "RADIUS: " << radius << std::endl;
-        float scale = (size)/radius;
-        
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-90.0f), osg::Vec3(1,0,0)) * osg::Matrix::scale(osg::Vec3(scale,scale,scale)) * osg::Matrix::translate(offset));
-        transform->addChild(model);
-        return transform;
-    } 
-    
-    static osg::MatrixTransform* CreateTexturedNormalMappedShape(osg::Vec3 offset, float size){
-        osg::Node* model = osgDB::readNodeFile(osgDB::findDataFile("Models/sphere.osg"));
-        if(!model){
-            OSG_FATAL << "ERROR: Failed to load model 'Models/sphere.osg' no normal mapping example avaliable." << std::endl;
-            return false;
-        }
-        
-        //generate tangent vectors
-        GenerateTangentsVisitor genTangents;
-        model->accept(genTangents);
-
-        osg::StateSet* state = model->getOrCreateStateSet();
-        state->setMode(GL_LIGHTING, osg::StateAttribute::ON);
-        state->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/diffuse.png"))), osg::StateAttribute::ON);
-        state->setTextureAttributeAndModes(1, new osg::Texture2D(osgDB::readImageFile(osgDB::findDataFile("Images/normal.png"))), osg::StateAttribute::ON);
-
-        //make sure the model fits the scene scale
-        float radius = model->computeBound().radius();
-        OSG_FATAL << "RADIUS: " << radius << std::endl;
-        float scale = (size)/radius;
-
-        
-        osg::MatrixTransform* transform = new osg::MatrixTransform();
-        transform->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-90.0f), osg::Vec3(1,0,0)) * osg::Matrix::scale(osg::Vec3(scale,scale,scale)) * osg::Matrix::translate(offset));
-        transform->addChild(model);
-        return transform;
-    } 
-    
-protected:
-
-};
\ No newline at end of file
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/InfoPlist.strings b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/InfoPlist.strings
deleted file mode 100644
index 477b28f..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,2 +0,0 @@
-/* Localized versions of Info.plist keys */
-
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPad.xib b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPad.xib
deleted file mode 100644
index 83de253..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPad.xib
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
-	<data>
-		<int key="IBDocument.SystemTarget">1296</int>
-		<string key="IBDocument.SystemVersion">11D50b</string>
-		<string key="IBDocument.InterfaceBuilderVersion">2182</string>
-		<string key="IBDocument.AppKitVersion">1138.32</string>
-		<string key="IBDocument.HIToolboxVersion">568.00</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			<string key="NS.object.0">1181</string>
-		</object>
-		<array key="IBDocument.IntegratedClassDependencies">
-			<string>IBProxyObject</string>
-			<string>IBUIView</string>
-		</array>
-		<array key="IBDocument.PluginDependencies">
-			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-		</array>
-		<object class="NSMutableDictionary" key="IBDocument.Metadata">
-			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
-			<integer value="1" key="NS.object.0"/>
-		</object>
-		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
-			<object class="IBProxyObject" id="372490531">
-				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
-				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
-			</object>
-			<object class="IBProxyObject" id="975951072">
-				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
-				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
-			</object>
-			<object class="IBUIView" id="191373211">
-				<reference key="NSNextResponder"/>
-				<int key="NSvFlags">274</int>
-				<string key="NSFrame">{{0, 20}, {768, 1004}}</string>
-				<reference key="NSSuperview"/>
-				<reference key="NSWindow"/>
-				<reference key="NSNextKeyView"/>
-				<object class="NSColor" key="IBUIBackgroundColor">
-					<int key="NSColorSpace">3</int>
-					<bytes key="NSWhite">MQA</bytes>
-					<object class="NSColorSpace" key="NSCustomColorSpace">
-						<int key="NSID">2</int>
-					</object>
-				</object>
-				<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics">
-					<int key="IBUIStatusBarStyle">2</int>
-				</object>
-				<string key="targetRuntimeIdentifier">IBIPadFramework</string>
-			</object>
-		</array>
-		<object class="IBObjectContainer" key="IBDocument.Objects">
-			<array class="NSMutableArray" key="connectionRecords">
-				<object class="IBConnectionRecord">
-					<object class="IBCocoaTouchOutletConnection" key="connection">
-						<string key="label">view</string>
-						<reference key="source" ref="372490531"/>
-						<reference key="destination" ref="191373211"/>
-					</object>
-					<int key="connectionID">3</int>
-				</object>
-			</array>
-			<object class="IBMutableOrderedSet" key="objectRecords">
-				<array key="orderedObjects">
-					<object class="IBObjectRecord">
-						<int key="objectID">0</int>
-						<array key="object" id="0"/>
-						<reference key="children" ref="1000"/>
-						<nil key="parent"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">1</int>
-						<reference key="object" ref="191373211"/>
-						<reference key="parent" ref="0"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-1</int>
-						<reference key="object" ref="372490531"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">File's Owner</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-2</int>
-						<reference key="object" ref="975951072"/>
-						<reference key="parent" ref="0"/>
-					</object>
-				</array>
-			</object>
-			<dictionary class="NSMutableDictionary" key="flattenedProperties">
-				<string key="-1.CustomClassName">ViewController</string>
-				<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="-2.CustomClassName">UIResponder</string>
-				<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			</dictionary>
-			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
-			<nil key="activeLocalization"/>
-			<dictionary class="NSMutableDictionary" key="localizations"/>
-			<nil key="sourceID"/>
-			<int key="maxID">3</int>
-		</object>
-		<object class="IBClassDescriber" key="IBDocument.Classes">
-			<array class="NSMutableArray" key="referencedPartialClassDescriptions">
-				<object class="IBPartialClassDescription">
-					<string key="className">ViewController</string>
-					<string key="superclassName">UIViewController</string>
-					<object class="IBClassDescriptionSource" key="sourceIdentifier">
-						<string key="majorKey">IBProjectSource</string>
-						<string key="minorKey">./Classes/ViewController.h</string>
-					</object>
-				</object>
-			</array>
-		</object>
-		<int key="IBDocument.localizationMode">0</int>
-		<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
-			<real value="1296" key="NS.object.0"/>
-		</object>
-		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
-		<int key="IBDocument.defaultPropertyAccessControl">3</int>
-		<string key="IBCocoaTouchPluginVersion">1181</string>
-	</data>
-</archive>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPhone.xib b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPhone.xib
deleted file mode 100644
index 78d0b27..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/en.lproj/ViewController_iPhone.xib
+++ /dev/null
@@ -1,124 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="8.00">
-	<data>
-		<int key="IBDocument.SystemTarget">1296</int>
-		<string key="IBDocument.SystemVersion">11E53</string>
-		<string key="IBDocument.InterfaceBuilderVersion">2182</string>
-		<string key="IBDocument.AppKitVersion">1138.47</string>
-		<string key="IBDocument.HIToolboxVersion">569.00</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			<string key="NS.object.0">1181</string>
-		</object>
-		<array key="IBDocument.IntegratedClassDependencies">
-			<string>IBProxyObject</string>
-			<string>IBUIView</string>
-		</array>
-		<array key="IBDocument.PluginDependencies">
-			<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-		</array>
-		<object class="NSMutableDictionary" key="IBDocument.Metadata">
-			<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
-			<integer value="1" key="NS.object.0"/>
-		</object>
-		<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
-			<object class="IBProxyObject" id="841351856">
-				<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-			<object class="IBProxyObject" id="371349661">
-				<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-			<object class="IBUIView" id="184854543">
-				<reference key="NSNextResponder"/>
-				<int key="NSvFlags">274</int>
-				<string key="NSFrameSize">{320, 460}</string>
-				<reference key="NSSuperview"/>
-				<reference key="NSWindow"/>
-				<reference key="NSNextKeyView"/>
-				<object class="NSColor" key="IBUIBackgroundColor">
-					<int key="NSColorSpace">3</int>
-					<bytes key="NSWhite">MQA</bytes>
-					<object class="NSColorSpace" key="NSCustomColorSpace">
-						<int key="NSID">2</int>
-					</object>
-				</object>
-				<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
-				<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
-			</object>
-		</array>
-		<object class="IBObjectContainer" key="IBDocument.Objects">
-			<array class="NSMutableArray" key="connectionRecords">
-				<object class="IBConnectionRecord">
-					<object class="IBCocoaTouchOutletConnection" key="connection">
-						<string key="label">view</string>
-						<reference key="source" ref="841351856"/>
-						<reference key="destination" ref="184854543"/>
-					</object>
-					<int key="connectionID">3</int>
-				</object>
-			</array>
-			<object class="IBMutableOrderedSet" key="objectRecords">
-				<array key="orderedObjects">
-					<object class="IBObjectRecord">
-						<int key="objectID">0</int>
-						<array key="object" id="0"/>
-						<reference key="children" ref="1000"/>
-						<nil key="parent"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-1</int>
-						<reference key="object" ref="841351856"/>
-						<reference key="parent" ref="0"/>
-						<string key="objectName">File's Owner</string>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">-2</int>
-						<reference key="object" ref="371349661"/>
-						<reference key="parent" ref="0"/>
-					</object>
-					<object class="IBObjectRecord">
-						<int key="objectID">2</int>
-						<reference key="object" ref="184854543"/>
-						<array class="NSMutableArray" key="children"/>
-						<reference key="parent" ref="0"/>
-					</object>
-				</array>
-			</object>
-			<dictionary class="NSMutableDictionary" key="flattenedProperties">
-				<string key="-1.CustomClassName">ViewController</string>
-				<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="-2.CustomClassName">UIResponder</string>
-				<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-				<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
-			</dictionary>
-			<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
-			<nil key="activeLocalization"/>
-			<dictionary class="NSMutableDictionary" key="localizations"/>
-			<nil key="sourceID"/>
-			<int key="maxID">6</int>
-		</object>
-		<object class="IBClassDescriber" key="IBDocument.Classes">
-			<array class="NSMutableArray" key="referencedPartialClassDescriptions">
-				<object class="IBPartialClassDescription">
-					<string key="className">ViewController</string>
-					<string key="superclassName">UIViewController</string>
-					<object class="IBClassDescriptionSource" key="sourceIdentifier">
-						<string key="majorKey">IBProjectSource</string>
-						<string key="minorKey">./Classes/ViewController.h</string>
-					</object>
-				</object>
-			</array>
-		</object>
-		<int key="IBDocument.localizationMode">0</int>
-		<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
-		<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
-			<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
-			<real value="1296" key="NS.object.0"/>
-		</object>
-		<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
-		<int key="IBDocument.defaultPropertyAccessControl">3</int>
-		<string key="IBCocoaTouchPluginVersion">1181</string>
-	</data>
-</archive>
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch
deleted file mode 100644
index 15e6a77..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgEarthViewerIOS-Prefix.pch
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// Prefix header for all source files of the 'osgEarthViewerIOS' target in the 'osgEarthViewerIOS' project
-//
-
-#import <Availability.h>
-
-#ifndef __IPHONE_5_0
-#warning "This project uses features only available in iOS SDK 5.0 and later."
-#endif
-
-#ifdef __OBJC__
-    #import <UIKit/UIKit.h>
-    #import <Foundation/Foundation.h>
-#endif
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
deleted file mode 100755
index 0dddc1c..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgPlugins.h
+++ /dev/null
@@ -1,84 +0,0 @@
-#pragma once 
-
-//This file is used to force the linking of the osg and hogbox plugins
-//as we our doing a static build we can't depend on the loading of the
-//dynamic libs to add the plugins to the registries
-
-#include <osgViewer/GraphicsWindow>
-#include <osgDB/Registry>
-
-//windowing system
-#ifndef ANDROID
-USE_GRAPICSWINDOW_IMPLEMENTATION(IOS)
-#endif
-
-
-//osg plugins
-
-USE_OSGPLUGIN(OpenFlight)
-USE_OSGPLUGIN(obj)
-USE_OSGPLUGIN(shp)
-USE_OSGPLUGIN(ive)
-
-//depreceated osg format
-USE_OSGPLUGIN(osg)
-USE_DOTOSGWRAPPER_LIBRARY(osg)
-
-
-USE_OSGPLUGIN(osg2)
-USE_SERIALIZER_WRAPPER_LIBRARY(osg)
-USE_SERIALIZER_WRAPPER_LIBRARY(osgAnimation)
-
-USE_OSGPLUGIN(rot)
-USE_OSGPLUGIN(scale)
-USE_OSGPLUGIN(trans)
-
-
-//image files
-#ifndef ANDROID
-USE_OSGPLUGIN(tiff)
-USE_OSGPLUGIN(imageio)
-#else
-USE_OSGPLUGIN(png)
-USE_OSGPLUGIN(jpeg)
-#endif
-
-USE_OSGPLUGIN(zip)
-USE_OSGPLUGIN(curl)
-USE_OSGPLUGIN(freetype)
-
-
-USE_OSGPLUGIN(kml)
-USE_OSGPLUGIN(osgearth_feature_wfs)
-USE_OSGPLUGIN(osgearth_feature_tfs)
-USE_OSGPLUGIN(osgearth_tms)
-USE_OSGPLUGIN(osgearth_wms)
-USE_OSGPLUGIN(osgearth_label_overlay)
-USE_OSGPLUGIN(osgearth_xyz)
-USE_OSGPLUGIN(osgearth_label_annotation)
-USE_OSGPLUGIN(osgearth_mask_feature)
-USE_OSGPLUGIN(osgearth_model_feature_geom)
-USE_OSGPLUGIN(osgearth_agglite)
-USE_OSGPLUGIN(osgearth_feature_ogr)
-USE_OSGPLUGIN(osgearth_model_feature_stencil)
-USE_OSGPLUGIN(osgearth_vdatum_egm2008)
-USE_OSGPLUGIN(osgearth_model_simple)
-//USE_OSGPLUGIN(osgearth_engine_osgterrain)
-USE_OSGPLUGIN(osgearth_engine_quadtree)
-USE_OSGPLUGIN(osgearth_engine_mp)
-USE_OSGPLUGIN(osgearth_vdatum_egm96)
-USE_OSGPLUGIN(osgearth_ocean_surface)
-USE_OSGPLUGIN(osgearth_debug)
-USE_OSGPLUGIN(osgearth_mbtiles)
-USE_OSGPLUGIN(osgearth_vdatum_egm84)
-USE_OSGPLUGIN(osgearth_tileservice)
-USE_OSGPLUGIN(osgearth_yahoo)
-USE_OSGPLUGIN(osgearth_arcgis_map_cache)
-USE_OSGPLUGIN(osgearth_tilecache)
-USE_OSGPLUGIN(osgearth_wcs)
-USE_OSGPLUGIN(osgearth_gdal)
-USE_OSGPLUGIN(earth)
-USE_OSGPLUGIN(osgearth_cache_filesystem)
-USE_OSGPLUGIN(osgearth_arcgis)
-USE_OSGPLUGIN(osgearth_osg)
-USE_OSGPLUGIN(osgearth_scriptengine_javascriptcore)
diff --git a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgearth_viewerIOS.cpp b/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgearth_viewerIOS.cpp
deleted file mode 100755
index 2e970bf..0000000
--- a/src/applications/osgearth_viewerIOS/osgEarthViewerIOS/osgearth_viewerIOS.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osg/Notify>
-#include <osgViewer/Viewer>
-#include <osgEarthUtil/EarthManipulator>
-#include <osgEarthUtil/ExampleResources>
-
-#define LC "[viewer] "
-
-using namespace osgEarth::Util;
-
-//------------------------------------------------------------------------
-
-int
-main(int argc, char** argv)
-{
-    osg::ArgumentParser arguments(&argc,argv);
-    if ( arguments.read("--stencil") )
-        osg::DisplaySettings::instance()->setMinimumNumStencilBits( 8 );
-
-    // create a viewer:
-    osgViewer::Viewer viewer(arguments);
-
-    // install our default manipulator (do this before calling load)
-    viewer.setCameraManipulator( new EarthManipulator() );
-
-    // load an earth file, and support all or our example command-line options
-    // and earth file <external> tags
-    osg::Node* node = MapNodeHelper().load( arguments, &viewer );
-    if ( node )
-    {
-        viewer.setSceneData( node );
-
-        // configure the near/far so we don't clip things that are up close
-        viewer.getCamera()->setNearFarRatio(0.00002);
-
-        // osgEarth benefits from pre-compilation of GL objects in the pager. In newer versions of
-        // OSG, this activates OSG's IncrementalCompileOpeartion in order to avoid frame breaks.
-        viewer.getDatabasePager()->setDoPreCompile( true );
-
-        return viewer.run();
-    }
-    else
-    {
-        OE_NOTICE 
-            << "\nUsage: " << argv[0] << " file.earth" << std::endl
-            << MapNodeHelper().usage() << std::endl;
-    }
-}
diff --git a/src/applications/osgearth_viewerIOS/osgPlugins.h b/src/applications/osgearth_viewerIOS/osgPlugins.h
new file mode 100755
index 0000000..89f93a9
--- /dev/null
+++ b/src/applications/osgearth_viewerIOS/osgPlugins.h
@@ -0,0 +1,206 @@
+#pragma once 
+
+//This file is used to force the linking of the osg plugins
+//as we our doing a static build we can't depend on the loading of the
+//dynamic libs to add the plugins to the registries
+
+#include <osgViewer/GraphicsWindow>
+#include <osgDB/Registry>
+#include <osgEarth/ColorFilter>
+#include <osgEarthFeatures/Filter>
+#include <osgEarthSymbology/Symbol>
+#include <osgEarthAnnotation/AnnotationRegistry>
+
+//windowing system
+#ifndef ANDROID
+USE_GRAPICSWINDOW_IMPLEMENTATION(IOS)
+#endif
+
+
+//osg plugins
+USE_OSGPLUGIN(zip)
+USE_OSGPLUGIN(curl)
+USE_OSGPLUGIN(freetype)
+
+USE_OSGPLUGIN(tiff)
+USE_OSGPLUGIN(rgb)
+#ifndef ANDROID
+USE_OSGPLUGIN(imageio)
+#else
+USE_OSGPLUGIN(png)
+USE_OSGPLUGIN(jpeg)
+USE_OSGPLUGIN(bmp);
+#endif
+
+USE_OSGPLUGIN(OpenFlight)
+USE_OSGPLUGIN(obj)
+USE_OSGPLUGIN(shp)
+//USE_OSGPLUGIN(ive)
+
+USE_OSGPLUGIN(osg)
+USE_DOTOSGWRAPPER_LIBRARY(osg)
+//USE_DOTOSGWRAPPER_LIBRARY(osgAnimation)
+USE_DOTOSGWRAPPER_LIBRARY(osgFX)
+//USE_DOTOSGWRAPPER_LIBRARY(osgParticle)
+USE_DOTOSGWRAPPER_LIBRARY(osgShadow)
+USE_DOTOSGWRAPPER_LIBRARY(osgSim)
+USE_DOTOSGWRAPPER_LIBRARY(osgTerrain)
+USE_DOTOSGWRAPPER_LIBRARY(osgText)
+USE_DOTOSGWRAPPER_LIBRARY(osgViewer)
+//USE_DOTOSGWRAPPER_LIBRARY(osgVolume)
+
+USE_OSGPLUGIN(osg2)
+USE_SERIALIZER_WRAPPER_LIBRARY(osg)
+//USE_SERIALIZER_WRAPPER_LIBRARY(osgAnimation)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgFX)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgManipulator)
+//USE_SERIALIZER_WRAPPER_LIBRARY(osgParticle)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgShadow)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgSim)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgTerrain)
+USE_SERIALIZER_WRAPPER_LIBRARY(osgText)
+//USE_SERIALIZER_WRAPPER_LIBRARY(osgUtil)
+//USE_SERIALIZER_WRAPPER_LIBRARY(osgViewer)
+//USE_SERIALIZER_WRAPPER_LIBRARY(osgVolume)
+
+USE_OSGPLUGIN(rot)
+USE_OSGPLUGIN(scale)
+USE_OSGPLUGIN(trans)
+
+// osgearth plugins
+USE_OSGPLUGIN(osgearth_agglite)
+USE_OSGPLUGIN(osgearth_arcgis)
+USE_OSGPLUGIN(osgearth_arcgis_map_cache)
+USE_OSGPLUGIN(osgearth_bing)
+USE_OSGPLUGIN(osgearth_bumpmap)
+USE_OSGPLUGIN(osgearth_cache_filesystem)
+USE_OSGPLUGIN(osgearth_colorramp)
+USE_OSGPLUGIN(osgearth_debug)
+USE_OSGPLUGIN(osgearth_detail)
+USE_OSGPLUGIN(earth)
+USE_OSGPLUGIN(osgearth_engine_byo)
+USE_OSGPLUGIN(osgearth_engine_mp)
+USE_OSGPLUGIN(osgearth_engine_rex)
+USE_OSGPLUGIN(osgearth_feature_elevation)
+USE_OSGPLUGIN(osgearth_feature_ogr)
+USE_OSGPLUGIN(osgearth_feature_raster)
+USE_OSGPLUGIN(osgearth_feature_tfs)
+USE_OSGPLUGIN(osgearth_feature_wfs)
+USE_OSGPLUGIN(osgearth_feature_xyz)
+USE_OSGPLUGIN(osgearth_featurefilter_intersect)
+USE_OSGPLUGIN(osgearth_featurefilter_join)
+USE_OSGPLUGIN(osgearth_gdal)
+USE_OSGPLUGIN(kml)
+USE_OSGPLUGIN(osgearth_label_annotation)
+USE_OSGPLUGIN(osgearth_mapinspector)
+USE_OSGPLUGIN(osgearth_mask_feature)
+USE_OSGPLUGIN(osgearth_mbtiles)
+USE_OSGPLUGIN(osgearth_model_feature_geom)
+USE_OSGPLUGIN(osgearth_model_simple)
+USE_OSGPLUGIN(osgearth_monitor)
+USE_OSGPLUGIN(osgearth_noise)
+USE_OSGPLUGIN(osgearth_ocean_simple)
+USE_OSGPLUGIN(osgearth_osg)
+USE_OSGPLUGIN(osgearth_quadkey)
+USE_OSGPLUGIN(osgearth_refresh)
+USE_OSGPLUGIN(osgearth_scriptengine_javascript)
+USE_OSGPLUGIN(osgearth_sky_gl)
+USE_OSGPLUGIN(osgearth_sky_simple)
+USE_OSGPLUGIN(osgearth_skyview)
+USE_OSGPLUGIN(osgearth_splat_mask)
+USE_OSGPLUGIN(template)
+USE_OSGPLUGIN(osgearth_template_matclass)
+USE_OSGPLUGIN(osgearth_terrainshader)
+USE_OSGPLUGIN(osgearth_tilecache)
+USE_OSGPLUGIN(osgearth_tileindex)
+USE_OSGPLUGIN(osgearth_tileservice)
+USE_OSGPLUGIN(osgearth_tms)
+USE_OSGPLUGIN(osgearth_vdatum_egm84)
+USE_OSGPLUGIN(osgearth_vdatum_egm96)
+USE_OSGPLUGIN(osgearth_vdatum_egm2008)
+USE_OSGPLUGIN(osgearth_viewpoints)
+USE_OSGPLUGIN(osgearth_vpb)
+USE_OSGPLUGIN(osgearth_wcs)
+USE_OSGPLUGIN(osgearth_wms)
+USE_OSGPLUGIN(osgearth_xyz)
+USE_OSGPLUGIN(osgearth_yahoo)
+
+// extensions
+//USE_OSGPLUGIN(osgearth_annotations)
+//USE_OSGPLUGIN(osgearth_bumpmap)
+USE_OSGPLUGIN(osgearth_bump_map)
+USE_OSGPLUGIN(osgearth_contourmap)
+USE_OSGPLUGIN(osgearth_contour_map)
+//USE_OSGPLUGIN(osgearth_sky_gl)
+//USE_OSGPLUGIN(osgearth_graticule)
+USE_OSGPLUGIN(osgearth_lod_blending)
+//USE_OSGPLUGIN(osgearth_mapinspector)
+//USE_OSGPLUGIN(osgearth_mgrs_graticule)
+//USE_OSGPLUGIN(osgearth_noise)
+USE_OSGPLUGIN(osgearth_screen_space_layout)
+USE_OSGPLUGIN(osgearth_decluttering)
+//USE_OSGPLUGIN(osgearth_ocean_simple)
+//USE_OSGPLUGIN(osgearth_sky_simple)
+//USE_OSGPLUGIN(osgearth_splat)
+//USE_OSGPLUGIN(osgearth_terrainshader)
+//USE_OSGPLUGIN(osgearth_utm_graticule)
+
+// annotations
+USE_OSGEARTH_ANNOTATION(circle)
+USE_OSGEARTH_ANNOTATION(ellipse)
+USE_OSGEARTH_ANNOTATION(feature)
+USE_OSGEARTH_ANNOTATION(imageoverlay)
+USE_OSGEARTH_ANNOTATION(label)
+USE_OSGEARTH_ANNOTATION(local_geometry)
+USE_OSGEARTH_ANNOTATION(place)
+USE_OSGEARTH_ANNOTATION(rectangle)
+
+// layers
+USE_OSGEARTH_LAYER(annotations)
+USE_OSGEARTH_LAYER(elevation)
+USE_OSGEARTH_LAYER(feature_mask)
+USE_OSGEARTH_LAYER(feature_model)
+USE_OSGEARTH_LAYER(feature_source)
+USE_OSGEARTH_LAYER(flattened_elevation)
+USE_OSGEARTH_LAYER(fractal_elevation)
+USE_OSGEARTH_LAYER(image)
+USE_OSGEARTH_LAYER(land_cover_dictionary)
+USE_OSGEARTH_LAYER(land_cover)
+USE_OSGEARTH_LAYER(model)
+USE_OSGEARTH_LAYER(multi_elevation)
+//USE_OSGEARTH_LAYER(road_surface)
+USE_OSGEARTH_LAYER(simple_ocean)
+USE_OSGEARTH_LAYER(video)
+
+// simple symbols
+USE_OSGEARTH_SIMPLE_SYMBOL(altitude)
+USE_OSGEARTH_SIMPLE_SYMBOL(bbox)
+USE_OSGEARTH_SIMPLE_SYMBOL(billboard)
+USE_OSGEARTH_SIMPLE_SYMBOL(coverage)
+USE_OSGEARTH_SIMPLE_SYMBOL(extrusion)
+USE_OSGEARTH_SIMPLE_SYMBOL(icon)
+USE_OSGEARTH_SIMPLE_SYMBOL(line)
+USE_OSGEARTH_SIMPLE_SYMBOL(marker)
+USE_OSGEARTH_SIMPLE_SYMBOL(model)
+USE_OSGEARTH_SIMPLE_SYMBOL(point)
+USE_OSGEARTH_SIMPLE_SYMBOL(polygon)
+USE_OSGEARTH_SIMPLE_SYMBOL(render)
+USE_OSGEARTH_SIMPLE_SYMBOL(skin)
+USE_OSGEARTH_SIMPLE_SYMBOL(text)
+
+// simple feature filters
+USE_OSGEARTH_SIMPLE_FEATUREFILTER(buffer)
+USE_OSGEARTH_SIMPLE_FEATUREFILTER(convert)
+USE_OSGEARTH_SIMPLE_FEATUREFILTER(resample)
+USE_OSGEARTH_SIMPLE_FEATUREFILTER(script)
+
+// color filters
+USE_OSGEARTH_COLORFILTER(brightness_contrast)
+USE_OSGEARTH_COLORFILTER(chroma_key)
+USE_OSGEARTH_COLORFILTER(cmyk)
+USE_OSGEARTH_COLORFILTER(gamma)
+USE_OSGEARTH_COLORFILTER(glsl)
+USE_OSGEARTH_COLORFILTER(hsl)
+USE_OSGEARTH_COLORFILTER(night)
+USE_OSGEARTH_COLORFILTER(rgb)
+
diff --git a/src/applications/osgearth_wfs/osgearth_wfs.cpp b/src/applications/osgearth_wfs/osgearth_wfs.cpp
index 351703b..7105550 100644
--- a/src/applications/osgearth_wfs/osgearth_wfs.cpp
+++ b/src/applications/osgearth_wfs/osgearth_wfs.cpp
@@ -32,13 +32,14 @@
 #include <osgEarthDrivers/feature_wfs/WFSFeatureOptions>
 
 // include for feature geometry model renderer:
-#include <osgEarthDrivers/model_feature_geom/FeatureGeomModelOptions>
+#include <osgEarthAnnotation/FeatureNode>
 #include <osgEarthSymbology/Style>
 
 
 #define LC "[wfs example] "
 
 using namespace osgEarth;
+using namespace osgEarth::Annotation;
 using namespace osgEarth::Util;
 using namespace osgEarth::Features;
 using namespace osgEarth::Symbology;
@@ -67,6 +68,14 @@ main(int argc, char** argv)
     viewer.getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
     viewer.getCamera()->setNearFarRatio(0.00002);
 
+    // Get the bounds from the command line.
+    Bounds bounds;
+    double xmin = DBL_MAX, ymin = DBL_MAX, xmax = DBL_MIN, ymax = DBL_MIN;
+    while (arguments.read("--bounds", xmin, ymin, xmax, ymax))
+    {
+        bounds.xMin() = xmin, bounds.yMin() = ymin, bounds.xMax() = xmax, bounds.yMax() = ymax;
+    }        
+
     // load an earth file, and support all or our example command-line options
     // and earth file <external> tags    
     osg::Node* node = MapNodeHelper().load( arguments, &viewer );
@@ -78,34 +87,35 @@ main(int argc, char** argv)
 
         // Create the WFS driver:
         osgEarth::Drivers::WFSFeatureOptions wfs;
-        wfs.url()          = osgEarth::URI("http://demo.opengeo.org/geoserver/wfs"); 
-        wfs.typeName()     = "states"; 
-        wfs.outputFormat() = "json";     // JSON or GML
-
-        // Configure a rendering style:
+        wfs.url()          = osgEarth::URI("http://demo.mapserver.org/cgi-bin/wfs"); 
+        wfs.typeName()     = "cities"; 
+        wfs.outputFormat() = "gml2";     // JSON or GML
+
+        // Create the feature source from the options
+        osg::ref_ptr< FeatureSource > featureSource = FeatureSourceFactory::create(wfs);
+        Status s = featureSource->open();
+
+        // Set the query with the bounds if one was specified.
+        Query query;
+        if (bounds.isValid())
+        {
+            query.bounds() = bounds;
+        }
+
+        // Get the features
+        osg::ref_ptr< FeatureCursor > cursor = featureSource->createFeatureCursor(query);
+        FeatureList features;
+        cursor->fill(features);
+        OE_NOTICE << "Got " << features.size() << " features" << std::endl;
+
+        // Create a style
         Style style;
-        style.setName( "states" ); 
-
-        LineSymbol* line = style.getOrCreate<LineSymbol>(); 
-        line->stroke()->color() = Color::Yellow; 
-        line->stroke()->width() = 5.0f;
-        line->stroke()->widthUnits() = Units::PIXELS;
-
-        AltitudeSymbol* alt = style.getOrCreate<AltitudeSymbol>();
-        alt->clamping()  = AltitudeSymbol::CLAMP_TO_TERRAIN;
-        alt->technique() = AltitudeSymbol::TECHNIQUE_DRAPE;
-
-        // Configure a model layer to render the features:
-        osgEarth::Drivers::FeatureGeomModelOptions geom; 
-        geom.featureOptions() = wfs;
-        geom.styles()         = new StyleSheet(); 
-        geom.styles()->addStyle(style); 
-
-        // Make the new layer and add it to the map.
-        ModelLayerOptions layerOptions("states", geom); 
-        ModelLayer* layer = new ModelLayer(layerOptions); 
-        mapNode->getMap()->addModelLayer(layer);
-        
+        style.getOrCreateSymbol<TextSymbol>()->content() = StringExpression("[NAME]");
+
+        // Create the FeatureNode with the features and the style.
+        osg::ref_ptr< FeatureNode > featureNode = new FeatureNode(mapNode, features, style);
+        mapNode->addChild(featureNode.get());                
+
         viewer.setSceneData( node );
         return viewer.run();
     }
diff --git a/src/osgEarth/AlphaEffect b/src/osgEarth/AlphaEffect
deleted file mode 100644
index 4c8b277..0000000
--- a/src/osgEarth/AlphaEffect
+++ /dev/null
@@ -1,73 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2008-2012 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-* IN THE SOFTWARE.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-#ifndef OSGEARTH_ALPHA_EFFECT_H
-#define OSGEARTH_ALPHA_EFFECT_H
-
-#include <osgEarth/Common>
-#include <osg/StateSet>
-#include <osg/Uniform>
-#include <osg/observer_ptr>
-
-namespace osgEarth
-{
-    /**
-     * Shader effect that lets you adjust the alpha channel.
-     */
-    class OSGEARTH_EXPORT AlphaEffect : public osg::Referenced
-    {
-    public:
-        /** constructs a new effect */
-        AlphaEffect();
-
-        /** contructs a new effect and attaches it to a stateset. */
-        AlphaEffect(osg::StateSet* stateset);
-
-    public:
-        /** The alpha channel value [0..1] */
-        void setAlpha(float value);
-        float getAlpha() const;
-
-    public:
-        /** attach this effect to a stateset. */
-        void attach(osg::StateSet* stateset);
-
-        /** detach this effect from any attached statesets. */
-        void detach();
-        /** detach this effect from a stateset. */
-        void detach(osg::StateSet* stateset);
-
-    protected:
-        virtual ~AlphaEffect();
-
-        typedef std::list< osg::observer_ptr<osg::StateSet> > StateSetList;
-
-        bool                       _installed;
-        StateSetList               _statesets;
-        osg::ref_ptr<osg::Uniform> _alphaUniform;
-
-        void init();
-        void install(osg::StateSet*);
-    };
-
-} // namespace osgEarth::Util
-
-#endif // OSGEARTH_ALPHA_EFFECT_H
diff --git a/src/osgEarth/AlphaEffect.cpp b/src/osgEarth/AlphaEffect.cpp
deleted file mode 100644
index 1dbb5e6..0000000
--- a/src/osgEarth/AlphaEffect.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
-* Copyright 2016 Pelican Mapping
-* http://osgearth.org
-*
-* osgEarth is free software; you can redistribute it and/or modify
-* it under the terms of the GNU Lesser General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-* IN THE SOFTWARE.
-*
-* You should have received a copy of the GNU Lesser General Public License
-* along with this program.  If not, see <http://www.gnu.org/licenses/>
-*/
-
-#include <osgEarth/AlphaEffect>
-#include <osgEarth/StringUtils>
-#include <osgEarth/VirtualProgram>
-#include <osgEarth/Registry>
-#include <osgEarth/Capabilities>
-#include <osgEarth/Shaders>
-
-using namespace osgEarth;
-
-#define LC "[AlphaEffect] "
-
-AlphaEffect::AlphaEffect()
-{
-    init();
-}
-
-AlphaEffect::AlphaEffect(osg::StateSet* stateset)
-{
-    init();
-    attach( stateset );
-}
-
-void
-AlphaEffect::init()
-{
-    _installed = false;
-    _alphaUniform = new osg::Uniform(osg::Uniform::FLOAT, "oe_alphaEffect_alpha");
-    _alphaUniform->set( 1.0f );
-}
-
-AlphaEffect::~AlphaEffect()
-{
-    detach();
-}
-
-void
-AlphaEffect::setAlpha(float value)
-{
-    float oldValue;
-    _alphaUniform->get(oldValue);
-
-    if (value != oldValue)
-    {        
-        _alphaUniform->set( value );
-
-        if (!_installed)
-        {
-            for (StateSetList::iterator it = _statesets.begin(); it != _statesets.end(); ++it)
-            {
-                osg::ref_ptr<osg::StateSet> stateset;
-                if ((*it).lock(stateset))
-                {
-                    install(stateset.get());
-                }
-            }
-            _installed = true;
-        }
-    }
-}
-
-float
-AlphaEffect::getAlpha() const
-{
-    float value = 1.0f;
-    _alphaUniform->get(value);
-    return value;
-}
-
-void
-AlphaEffect::attach(osg::StateSet* stateset)
-{
-    if ( stateset )
-    {
-        _statesets.push_back(stateset);
-        if (_installed)
-        {
-            install(stateset);
-        }
-    }
-}
-
-void
-AlphaEffect::detach()
-{
-    if (_installed)
-    {
-        for (StateSetList::iterator it = _statesets.begin(); it != _statesets.end(); ++it)
-        {
-            osg::ref_ptr<osg::StateSet> stateset;
-            if ( (*it).lock(stateset) )
-            {
-                detach( stateset );
-                (*it) = 0L;
-            }
-        }
-    }
-    _statesets.clear();
-}
-
-void
-AlphaEffect::detach(osg::StateSet* stateset)
-{
-    if ( stateset && _installed )
-    {
-        stateset->removeUniform( _alphaUniform.get() );
-        VirtualProgram* vp = VirtualProgram::get( stateset );
-        if ( vp )
-        {
-            Shaders pkg;
-            pkg.unload( vp, pkg.AlphaEffectFragment );
-        }
-    }
-}
-
-void
-AlphaEffect::install(osg::StateSet* stateset)
-{
-    if (stateset)
-    {
-        VirtualProgram* vp = VirtualProgram::getOrCreate(stateset);
-        if (vp)
-        {
-            vp->setName( "osgEarth.AlphaEffect" );
-            Shaders pkg;
-            pkg.load(vp, pkg.AlphaEffectFragment);
-            stateset->addUniform(_alphaUniform.get());
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/osgEarth/AlphaEffect.frag.glsl b/src/osgEarth/AlphaEffect.frag.glsl
deleted file mode 100644
index 1c90358..0000000
--- a/src/osgEarth/AlphaEffect.frag.glsl
+++ /dev/null
@@ -1,12 +0,0 @@
-#version $GLSL_VERSION_STR
-
-#pragma vp_entryPoint oe_alphaEffect_frag
-#pragma vp_location   fragment_coloring
-#pragma vp_order      0.5
-
-uniform float oe_alphaEffect_alpha;
-
-void oe_alphaEffect_frag(inout vec4 color)
-{
-    color = color * oe_alphaEffect_alpha;
-}
diff --git a/src/osgEarth/AutoScale.cpp b/src/osgEarth/AutoScale.cpp
deleted file mode 100644
index 7dbdbe5..0000000
--- a/src/osgEarth/AutoScale.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2016 Pelican Mapping
- * http://osgearth.org
- *
- * osgEarth is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>
- */
-#include <osgEarth/AutoScale>
-#include <osgEarth/ThreadingUtils>
-#include <osgEarth/Utils>
-#include <osgEarth/VirtualProgram>
-#include <osgEarth/Registry>
-#include <osgEarth/ShaderFactory>
-#include <osgUtil/RenderBin>
-
-#define LC "[AutoScale] "
-
-using namespace osgEarth;
-
-#define AUTO_SCALE_BIN_NAME "osgEarth::AutoScale"
-const std::string osgEarth::AUTO_SCALE_BIN = AUTO_SCALE_BIN_NAME;
-
-namespace
-{
-    const char* vs =
-        "#version " GLSL_VERSION_STR "\n"
-        GLSL_DEFAULT_PRECISION_FLOAT "\n"
-
-        "uniform float oe_autoscale_zp; \n"
-
-        "void oe_autoscale_vertex( inout vec4 VertexVIEW ) \n"
-        "{ \n"
-        "    float z       = -VertexVIEW.z; \n"
-
-        "    vec4  cp       = gl_ModelViewMatrix * vec4(0.0,0.0,0.0,1.0); \n" // control point into view space
-        "    float d        = length(cp.xyz); \n"
-        "    vec3  cpn      = cp.xyz/d; \n"
-        "    vec3  off      = VertexVIEW.xyz - cp.xyz; \n"
-
-        "    float dp = (d * oe_autoscale_zp) / z; \n"
-        "    cp.xyz   = cpn * dp; \n"
-
-        "    VertexVIEW.z *= (oe_autoscale_zp/z); \n"
-        "    VertexVIEW.xy = cp.xy + off.xy; \n"
-
-        "    vec3 push      = normalize(VertexVIEW.xyz); \n"
-        "    VertexVIEW.xyz = push * z; \n"
-        "} \n";
-
-
-    class AutoScaleRenderBin : public osgUtil::RenderBin
-    {
-    public:
-        osg::ref_ptr<osg::Uniform>  _zp;
-
-        // support cloning (from RenderBin):
-        virtual osg::Object* cloneType() const { return new AutoScaleRenderBin(); }
-        virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new AutoScaleRenderBin(*this,copyop); } // note only implements a clone of type.
-        virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const AutoScaleRenderBin*>(obj)!=0L; }
-        virtual const char* libraryName() const { return "osgEarth"; }
-        virtual const char* className() const { return "AutoScaleRenderBin"; }
-
-
-        // constructs the prototype for this render bin.
-        AutoScaleRenderBin() : osgUtil::RenderBin()
-        {
-            //OE_NOTICE << LC << "AUTOSCALE: created bin." << std::endl;
-
-            this->setName( osgEarth::AUTO_SCALE_BIN );
-
-            _stateset = new osg::StateSet();
-
-            VirtualProgram* vp = VirtualProgram::getOrCreate(_stateset.get());
-            vp->setFunction( "oe_autoscale_vertex", vs, ShaderComp::LOCATION_VERTEX_VIEW, 0L, 0.5f );
-
-            _zp = _stateset->getOrCreateUniform("oe_autoscale_zp", osg::Uniform::FLOAT);
-        }
-
-        AutoScaleRenderBin( const AutoScaleRenderBin& rhs, const osg::CopyOp& op )
-            : osgUtil::RenderBin( rhs, op ),
-              _zp      ( rhs._zp.get() )
-        {
-            //nop
-            //OE_NOTICE << LC << "AUTOSCALE: cloned bin." << std::endl;
-        }
-
-        /**
-         * Draws a bin. Most of this code is copied from osgUtil::RenderBin::drawImplementation.
-         * The modifications are (a) skipping code to render child bins, (b) setting a bin-global
-         * projection matrix in orthographic space, and (c) calling our custom "renderLeaf()" method 
-         * instead of RenderLeaf::render()
-         * (override)
-         */
-        void drawImplementation( osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
-        {
-            //OE_NOTICE << LC << "AUTOSCALE: leaves = " << getNumLeaves(this) << std::endl;
-
-            // apply a window-space projection matrix.
-            const osg::Viewport* vp = renderInfo.getCurrentCamera()->getViewport();
-            if ( vp )
-            {
-                float vpw = 0.5f*(float)vp->width();
-
-                double fovy, aspectRatio, n, f;
-                renderInfo.getCurrentCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, n, f);
-                float hfovd2 = (float)(0.5*fovy*aspectRatio);
-
-                _zp->set( vpw / tanf(osg::DegreesToRadians(hfovd2)) );
-            }
-    
-            osgUtil::RenderBin::drawImplementation(renderInfo, previous);
-        }
-
-        // debugging - counts the total # of leaves in this bin and its children
-        static unsigned getNumLeaves(osgUtil::RenderBin* bin)
-        {
-            unsigned here = bin->getRenderLeafList().size();
-            for( RenderBinList::iterator i = bin->getRenderBinList().begin(); i != bin->getRenderBinList().end(); ++i )
-                here += getNumLeaves( i->second.get() );
-            return here;
-        }
-    };
-}
-
-/** static registration of the bin */
-//extern "C" void osgEarth_AutoScaleBin_registration(void) {}
-//static osgEarthRegisterRenderBinProxy<AutoScaleRenderBin> s_regbin(AUTO_SCALE_BIN_NAME);
diff --git a/src/osgEarth/Bounds.cpp b/src/osgEarth/Bounds.cpp
index 3f5a8b0..a7e9700 100644
--- a/src/osgEarth/Bounds.cpp
+++ b/src/osgEarth/Bounds.cpp
@@ -147,7 +147,7 @@ Bounds::radius2d() const {
 
 double
 Bounds::area2d() const {
-    return width() * height();
+    return isValid() ? width() * height() : -1.0;
 }
 
 std::string
diff --git a/src/osgEarth/CMakeLists.txt b/src/osgEarth/CMakeLists.txt
index 4104e34..b4afa62 100644
--- a/src/osgEarth/CMakeLists.txt
+++ b/src/osgEarth/CMakeLists.txt
@@ -4,9 +4,18 @@ ELSE (DYNAMIC_OSGEARTH)
     ADD_DEFINITIONS(-DOSGEARTH_LIBRARY_STATIC)
 ENDIF(DYNAMIC_OSGEARTH)
 
+# CURL options
 OPTION(CURL_IS_STATIC "on if curl is a static lib " ON)
 MARK_AS_ADVANCED(CURL_IS_STATIC)
 
+# NVTT mipmap generation 
+option(OSGEARTH_ENABLE_NVTT_CPU_MIPMAPS "Use NVTT, if available, to generate texture mipmaps on the CPU" OFF)
+mark_as_advanced(OSGEARTH_ENABLE_NVTT_CPU_MIPMAPS)
+if(OSGEARTH_ENABLE_NVTT_CPU_MIPMAPS)
+    add_definitions(-DOSGEARTH_ENABLE_NVTT_CPU_MIPMAPS)
+endif(OSGEARTH_ENABLE_NVTT_CPU_MIPMAPS)
+
+# TinyXML options
 ADD_DEFINITIONS(-DTIXML_USE_STL)
 
 # Builds the HTTPClient with WIN_INET instead of CURL
@@ -29,14 +38,15 @@ ENDIF(WIN32)
 SET(LIB_NAME osgEarth)
 
 set(TARGET_GLSL
-    AlphaEffect.frag.glsl
     DepthOffset.vert.glsl
     Draping.vert.glsl
     Draping.frag.glsl
     GPUClamping.vert.glsl
     GPUClamping.vert.lib.glsl
     GPUClamping.frag.glsl
-    Instancing.vert.glsl)
+    Instancing.vert.glsl
+    PhongLighting.vert.glsl
+    PhongLighting.frag.glsl)
 
 set(SHADERS_CPP "${CMAKE_CURRENT_BINARY_DIR}/AutoGenShaders.cpp")
 
@@ -51,8 +61,6 @@ configure_shaders(
 SET(HEADER_PATH ${OSGEARTH_SOURCE_DIR}/include/${LIB_NAME})
 
 SET(LIB_PUBLIC_HEADERS
-    AlphaEffect
-    AutoScale
     Bounds
     Cache
     CacheEstimator
@@ -73,7 +81,6 @@ SET(LIB_PUBLIC_HEADERS
     DateTime
     DateTimeRange
     DepthOffset
-    DPLineSegmentIntersector
     DrapeableNode
     DrapingCullSet
     DrapingTechnique
@@ -81,7 +88,9 @@ SET(LIB_PUBLIC_HEADERS
     ECEF
     ElevationLayer
     ElevationLOD
+    ElevationPool
     ElevationQuery
+    Endian
     Export
     Extension
     FadeEffect
@@ -100,9 +109,14 @@ SET(LIB_PUBLIC_HEADERS
     ImageMosaic
     ImageToHeightFieldConverter
     ImageUtils
+    IntersectionPicker
     IOTypes
     JsonUtils
+    LandCover
+    LandCoverLayer
     Layer
+    LayerListener
+    Lighting
     LineFunctor
     Locators
     LocalTangentPlane
@@ -120,6 +134,8 @@ SET(LIB_PUBLIC_HEADERS
     MaskSource
     Memory
     MemCache
+    MetaTile
+    Metrics
     ModelLayer
     ModelSource
     NativeProgramAdapter
@@ -128,19 +144,20 @@ SET(LIB_PUBLIC_HEADERS
     optional
     ObjectIndex
     OverlayDecorator
-    OverlayNode
+    PagedNode
+    PatchLayer
     PhongLightingEffect
     Picker
-    IntersectionPicker
+    PluginLoader
     PrimitiveIntersector
     Profile
     Profiler
     Progress
-    QuadTree
     Random
     Registry
     ResourceReleaser
     Revisioning
+    SceneGraphCallback
     ScreenSpaceLayout
     Shaders
     ShaderFactory
@@ -149,6 +166,7 @@ SET(LIB_PUBLIC_HEADERS
     ShaderUtils
     Shadowing
     SharedSARepo
+    SimplexNoise
     SpatialReference
     StateSetCache
     StateSetLOD
@@ -161,15 +179,15 @@ SET(LIB_PUBLIC_HEADERS
     TerrainOptions
     TerrainEngineNode
     TerrainEngineRequirements
+    TerrainResources
     TerrainTileModel
     TerrainTileModelFactory
     TerrainTileNode
     TileKeyDataStore
-    TilePatchCallback
     Tessellator
-    TextureCompositor
     TileKey
     TileHandler
+    TileRasterizer
     TileSource
     TileVisitor
     TimeControl
@@ -180,8 +198,11 @@ SET(LIB_PUBLIC_HEADERS
     Utils
     Version
     VerticalDatum
+    VideoLayer
     Viewpoint
     VirtualProgram
+    VisibleLayer
+    WrapperLayer
     XmlUtils
 )
 
@@ -190,7 +211,7 @@ IF (NOT TINYXML_FOUND)
     SET(LIB_PUBLIC_HEADERS
         ${LIB_PUBLIC_HEADERS}
         tinystr.h
-        tinyxml.h 
+        tinyxml.h
     )
 ENDIF (NOT TINYXML_FOUND)
 
@@ -207,19 +228,17 @@ ENDIF (NOT TINYXML_FOUND)
 SET(VERSION_GIT_SOURCE "")
 IF (OSGEARTH_EMBED_GIT_SHA)
 	ADD_DEFINITIONS(-DOSGEARTH_EMBED_GIT_SHA)
-	
+
 	# auto-generate the VersionGit file to include the git SHA1 variable.
 	configure_file(
 		"${CMAKE_CURRENT_SOURCE_DIR}/VersionGit.cpp.in"
-		"${CMAKE_CURRENT_BINARY_DIR}/VersionGit.cpp" 
+		"${CMAKE_CURRENT_BINARY_DIR}/VersionGit.cpp"
 		@ONLY)
-    
-	set(VERSION_GIT_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/VersionGit.cpp")	
+
+	set(VERSION_GIT_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/VersionGit.cpp")
 ENDIF (OSGEARTH_EMBED_GIT_SHA)
 
 set(TARGET_SRC
-    AlphaEffect.cpp
-    AutoScale.cpp
     Bounds.cpp
     Cache.cpp
     CacheBin.cpp
@@ -238,7 +257,6 @@ set(TARGET_SRC
     DateTime.cpp
     DateTimeRange.cpp
     DepthOffset.cpp
-    DPLineSegmentIntersector.cpp
     DrapeableNode.cpp
     DrapingCullSet.cpp
     DrapingTechnique.cpp
@@ -246,6 +264,7 @@ set(TARGET_SRC
     ECEF.cpp
     ElevationLayer.cpp
     ElevationLOD.cpp
+    ElevationPool.cpp
     ElevationQuery.cpp
     Extension.cpp
     FadeEffect.cpp
@@ -263,9 +282,13 @@ set(TARGET_SRC
     ImageMosaic.cpp
     ImageToHeightFieldConverter.cpp
     ImageUtils.cpp
+    IntersectionPicker.cpp
     IOTypes.cpp
     JsonUtils.cpp
+    LandCover.cpp
+    LandCoverLayer.cpp
     Layer.cpp
+    Lighting.cpp
     Locators.cpp
     LocalTangentPlane.cpp
     Map.cpp
@@ -280,6 +303,8 @@ set(TARGET_SRC
     MaskSource.cpp
     MemCache.cpp
     Memory.cpp
+    MetaTile.cpp
+    Metrics.cpp
     MimeTypes.cpp
     ModelLayer.cpp
     ModelSource.cpp
@@ -287,24 +312,25 @@ set(TARGET_SRC
     Notify.cpp
     ObjectIndex.cpp
     OverlayDecorator.cpp
-    OverlayNode.cpp
+    PagedNode.cpp
+    PatchLayer.cpp
     PhongLightingEffect.cpp
-    IntersectionPicker.cpp
     PrimitiveIntersector.cpp
     Profile.cpp
     Profiler.cpp
     Progress.cpp
-    QuadTree.cpp
     Random.cpp
     Registry.cpp
     ResourceReleaser.cpp
     Revisioning.cpp
+    SceneGraphCallback.cpp
     ScreenSpaceLayout.cpp
     ShaderFactory.cpp
     ShaderGenerator.cpp
     ShaderLoader.cpp
     ShaderUtils.cpp
     Shadowing.cpp
+    SimplexNoise.cpp
     SpatialReference.cpp
     StateSetCache.cpp
     StateSetLOD.cpp
@@ -315,14 +341,14 @@ set(TARGET_SRC
     TerrainLayer.cpp
     TerrainOptions.cpp
     TerrainEngineNode.cpp
+    TerrainResources.cpp
     TerrainTileModel.cpp
     TerrainTileModelFactory.cpp
     Tessellator.cpp
     TextureBufferSerializer.cpp
-    TextureCompositor.cpp
     TileKey.cpp
     TileHandler.cpp
-    TilePatchCallback.cpp
+    TileRasterizer.cpp
     TileVisitor.cpp
     TileSource.cpp
     TimeControl.cpp
@@ -333,8 +359,10 @@ set(TARGET_SRC
     Utils.cpp
     Version.cpp
     VerticalDatum.cpp
+    VideoLayer.cpp
     Viewpoint.cpp
     VirtualProgram.cpp
+    VisibleLayer.cpp
     XmlUtils.cpp
     ${SHADERS_CPP} )
 
diff --git a/src/osgEarth/Cache b/src/osgEarth/Cache
index 10d36c6..cab05e6 100644
--- a/src/osgEarth/Cache
+++ b/src/osgEarth/Cache
@@ -36,6 +36,8 @@
 #define OSGEARTH_ENV_CACHE_DRIVER  "OSGEARTH_CACHE_DRIVER"
 #define OSGEARTH_ENV_CACHE_PATH    "OSGEARTH_CACHE_PATH"
 #define OSGEARTH_ENV_CACHE_ONLY    "OSGEARTH_CACHE_ONLY"
+#define OSGEARTH_ENV_DEFAULT_COMPRESSOR "OSGEARTH_DEFAULT_COMPRESSOR"
+
 #define OSGEARTH_ENV_NO_CACHE      "OSGEARTH_NO_CACHE"
 #define OSGEARTH_ENV_CACHE_MAX_AGE "OSGEARTH_CACHE_MAX_AGE"
 
@@ -69,7 +71,7 @@ namespace osgEarth
         /** Whether this object support an active cache. */
         bool isCacheEnabled() const;
         bool isCacheDisabled() const { return !isCacheEnabled(); }
-        
+
         /** Cache used by these settings */
         Cache* getCache() const { return _cache.get(); }
         void setCache(Cache* cache) { _cache = cache; }
@@ -86,9 +88,11 @@ namespace osgEarth
         /** Integrates an outside cache policy with the one in these settings. This method
           * also takes care of global (registry) override policy. */
         void integrateCachePolicy(const optional<CachePolicy>& policy);
-        
-        /** Get/Set the settings in a read-options structure. */
+
+        /** Extracts the settings from a read-options structure. */
         static CacheSettings* get(const osgDB::Options* readOptions);
+
+        /** Stores this instance in an Options data container. Extract using get(). */
         void store(osgDB::Options* readOptions);
 
         /** for debugging */
@@ -113,8 +117,8 @@ namespace osgEarth
     public:
         CacheOptions( const ConfigOptions& options =ConfigOptions() )
             : DriverConfigOptions( options )
-        { 
-            fromConfig( _conf ); 
+        {
+            fromConfig( _conf );
         }
 
         /** dtor */
@@ -127,7 +131,7 @@ namespace osgEarth
         }
 
         virtual void mergeConfig( const Config& conf ) {
-            ConfigOptions::mergeConfig( conf );            
+            ConfigOptions::mergeConfig( conf );
             fromConfig( conf );
         }
 
@@ -167,7 +171,7 @@ namespace osgEarth
          */
         CacheBin* getBin( const std::string& name );
 
-        /** 
+        /**
          * Gets the default caching bin within this cache. This may or may not
          * be supported by the implementation, so be sure to check the result
          * before using it.
@@ -187,7 +191,7 @@ namespace osgEarth
          */
         virtual void removeBin( CacheBin* bin );
 
-        /** 
+        /**
          * Gets an Options structure representing this cache's configuration.
          */
         const CacheOptions& getCacheOptions() const { return _options; }
@@ -232,7 +236,7 @@ namespace osgEarth
 
 //----------------------------------------------------------------------
 
-    /** 
+    /**
      * Factory class that can load and instantiate a Cache implementation based on the
      * information in the CacheOptions settings.
      */
diff --git a/src/osgEarth/Cache.cpp b/src/osgEarth/Cache.cpp
index aacb299..1c19ef7 100644
--- a/src/osgEarth/Cache.cpp
+++ b/src/osgEarth/Cache.cpp
@@ -151,7 +151,7 @@ Cache::removeBin( CacheBin* bin )
 Cache*
 CacheFactory::create( const CacheOptions& options )
 {
-    osg::ref_ptr<Cache> result =0L;
+    osg::ref_ptr<Cache> result;
     OE_DEBUG << LC << "Initializing cache of type \"" << options.getDriver() << "\"" << std::endl;
 
     if ( options.getDriver().empty() )
@@ -168,8 +168,8 @@ CacheFactory::create( const CacheOptions& options )
         rwopt->setPluginData( CACHE_OPTIONS_TAG, (void*)&options );
 
         std::string driverExt = std::string(".osgearth_cache_") + options.getDriver();
-        osgDB::ReaderWriter::ReadResult rr = osgDB::readObjectFile( driverExt, rwopt.get() );
-        result = dynamic_cast<Cache*>( rr.getObject() );
+        osg::ref_ptr<osg::Object> object = osgDB::readRefObjectFile( driverExt, rwopt.get() );
+        result = dynamic_cast<Cache*>( object.release() );
         if ( !result.valid() )
         {
             OE_WARN << LC << "Failed to load cache plugin for type \"" << options.getDriver() << "\"" << std::endl;
diff --git a/src/osgEarth/CacheBin.cpp b/src/osgEarth/CacheBin.cpp
index a2faf82..a5082a7 100644
--- a/src/osgEarth/CacheBin.cpp
+++ b/src/osgEarth/CacheBin.cpp
@@ -32,6 +32,21 @@
 
 using namespace osgEarth;
 
+
+// serializer for osg::DummyObject (not present in OSG)
+// We need this because the osgDB::DatabasePager will sometimes
+// add a DummyObject to textures that it finds in paged objects.
+namespace osg
+{
+    REGISTER_OBJECT_WRAPPER(DummyObject,
+                            new osg::DummyObject,
+                            osg::DummyObject,
+                            "osg::DummyObject")
+    {
+        //nop
+    }
+}
+
 namespace
 {
 #undef  LC
@@ -131,6 +146,8 @@ namespace
                         {              
                             tex->setUnRefImageDataAfterApply(false);               
 
+#if 0 // took this out in favor of the osg::DummyObject serializer above.
+
                             // OSG's DatabasePager attaches "marker objects" to Textures' UserData when it runs a
                             // FindCompileableGLObjectsVisitor. This operation is not thread-safe; it doesn't
                             // account for the possibility that the texture may already be in use elsewhere.
@@ -144,7 +161,6 @@ namespace
                             // This "hack" prevents a crash in OSG 3.4.0 when trying to modify and then write
                             // serialize the scene graph containing these shared texture objects.
                             // Kudos to Jason B for figuring this one out.
-
                             osg::Texture* texClone = osg::clone(tex, osg::CopyOp::SHALLOW_COPY);
                             if ( texClone )
                             {
@@ -165,6 +181,7 @@ namespace
                             {
                                 OE_WARN << LC << "Texture clone failed.\n";
                             }
+#endif
                         }
                         else
                         {
diff --git a/src/osgEarth/CacheEstimator.cpp b/src/osgEarth/CacheEstimator.cpp
index e5ba765..7ae1552 100644
--- a/src/osgEarth/CacheEstimator.cpp
+++ b/src/osgEarth/CacheEstimator.cpp
@@ -50,8 +50,6 @@ CacheEstimator::getNumTiles() const
 
     for (unsigned int level = _minLevel; level <= _maxLevel; level++)
     {
-        double coverageRatio = 0.0;
-
         if (_extents.empty())
         {
             unsigned int wide, high;
@@ -63,7 +61,6 @@ CacheEstimator::getNumTiles() const
             for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); ++itr)
             {
                 const GeoExtent& extent = *itr;
-                double boundsArea = extent.area();
 
                 TileKey ll = _profile->createTileKey(extent.xMin(), extent.yMin(), level);
                 TileKey ur = _profile->createTileKey(extent.xMax(), extent.yMax(), level);
diff --git a/src/osgEarth/CachePolicy b/src/osgEarth/CachePolicy
index 720184b..fb72ab4 100644
--- a/src/osgEarth/CachePolicy
+++ b/src/osgEarth/CachePolicy
@@ -66,12 +66,6 @@ namespace osgEarth
         /** constructs a CachePolicy from a config options */
         CachePolicy( const Config& conf );
 
-        /** construct a cache policy be reading it from an osgDB::Options */
-        //static optional<CachePolicy> get(const osgDB::Options* options);
-
-        /** Stores this cache policy in a DB Options. */
-        //void store( osgDB::Options* options ) const;
-
         /** Merges any set properties in another CP into this one, override existing values. */
         void mergeAndOverride(const CachePolicy& rhs);
         void mergeAndOverride(const optional<CachePolicy>& rhs);
@@ -79,9 +73,7 @@ namespace osgEarth
         /** Gets the oldest timestamp for which to accept a cache record */
         TimeStamp getMinAcceptTime() const;
 
-        /**
-         * Determine whether the given timestamp is considered to be expired based on this CachePolicy
-         */
+        /** Whether the given timestamp is considered to be expired based on this CachePolicy */
         bool isExpired(TimeStamp lastModified) const;
 
         /** dtor */
diff --git a/src/osgEarth/CachePolicy.cpp b/src/osgEarth/CachePolicy.cpp
index ab89349..b6194a6 100644
--- a/src/osgEarth/CachePolicy.cpp
+++ b/src/osgEarth/CachePolicy.cpp
@@ -63,31 +63,6 @@ _minTime( rhs._minTime )
     //nop
 }
 
-//optional<CachePolicy>
-//CachePolicy::get(const osgDB::Options* readOptions)
-//{
-//    optional<CachePolicy> policy;
-//    if (readOptions)
-//    {
-//        CacheSettings* settings = CacheSettings::get(readOptions);
-//        if (settings)
-//        {
-//            policy = settings->cachePolicy().get();
-//        }
-//    }
-//    return policy;
-//}
-
-//void
-//CachePolicy::store(osgDB::Options* dbOptions) const
-//{
-//    if ( dbOptions )
-//    {
-//        Config conf = getConfig();
-//        dbOptions->setPluginStringData( "osgEarth::CachePolicy", conf.toJSON() );
-//    }
-//}
-
 void
 CachePolicy::mergeAndOverride(const CachePolicy& rhs)
 {
diff --git a/src/osgEarth/CacheSeed b/src/osgEarth/CacheSeed
index 11d179a..da642d3 100644
--- a/src/osgEarth/CacheSeed
+++ b/src/osgEarth/CacheSeed
@@ -24,20 +24,21 @@
 #define OSGEARTH_CACHE_SEED_H 1
 
 #include <osgEarth/Common>
-#include <osgEarth/Map>
 #include <osgEarth/TileKey>
 #include <osgEarth/TileVisitor>
 
 
 namespace osgEarth
 {
+    class Map;
+
     /**
     * A TileHandler that caches tiles for the given layer.
     */
     class OSGEARTH_EXPORT CacheTileHandler : public TileHandler
     {
     public:
-        CacheTileHandler( TerrainLayer* layer, Map* map );
+        CacheTileHandler( TerrainLayer* layer, const Map* map );
         virtual bool handleTile( const TileKey& key, const TileVisitor& tv );
         virtual bool hasData( const TileKey& key ) const;
 
@@ -45,7 +46,7 @@ namespace osgEarth
 
     protected:
         osg::ref_ptr< TerrainLayer > _layer;
-        osg::ref_ptr< Map > _map;
+        osg::ref_ptr< const Map > _map;
     };    
 
     /**
@@ -69,7 +70,7 @@ namespace osgEarth
         /**
         * Seeds a TerrainLayer
         */
-        void run(TerrainLayer* layer, Map* map );
+        void run(TerrainLayer* layer, const Map* map );
 
 
     protected:
diff --git a/src/osgEarth/CacheSeed.cpp b/src/osgEarth/CacheSeed.cpp
index 58f1b99..693ad15 100644
--- a/src/osgEarth/CacheSeed.cpp
+++ b/src/osgEarth/CacheSeed.cpp
@@ -23,6 +23,9 @@
 #include <osgEarth/CacheSeed>
 #include <osgEarth/CacheEstimator>
 #include <osgEarth/MapFrame>
+#include <osgEarth/Map>
+#include <osgEarth/ImageLayer>
+#include <osgEarth/ElevationLayer>
 #include <OpenThreads/ScopedLock>
 #include <limits.h>
 
@@ -31,7 +34,7 @@
 using namespace osgEarth;
 using namespace OpenThreads;
 
-CacheTileHandler::CacheTileHandler( TerrainLayer* layer, Map* map ):
+CacheTileHandler::CacheTileHandler( TerrainLayer* layer, const Map* map ):
 _layer( layer ),
 _map( map )
 {
@@ -53,7 +56,7 @@ bool CacheTileHandler::handleTile(const TileKey& key, const TileVisitor& tv)
     }
     else if (elevationLayer )
     {
-        GeoHeightField hf = elevationLayer->createHeightField( key );
+        GeoHeightField hf = elevationLayer->createHeightField(key, 0L);
         if (hf.valid())
         {                
             return true;
@@ -62,7 +65,7 @@ bool CacheTileHandler::handleTile(const TileKey& key, const TileVisitor& tv)
 
     // If we didn't produce a result but the key isn't within range then we should continue to 
     // traverse the children b/c a min level was set.
-    if (!_layer->isKeyInRange(key))
+    if (!_layer->isKeyInLegalRange(key))
     {
         return true;
     }
@@ -72,41 +75,26 @@ bool CacheTileHandler::handleTile(const TileKey& key, const TileVisitor& tv)
 
 bool CacheTileHandler::hasData( const TileKey& key ) const
 {
-    TileSource* ts = _layer->getTileSource();
-    if (ts)
-    {
-        return ts->hasData(key);
-    }
-    return true;
+    return _layer->mayHaveData(key);
 }
 
 std::string CacheTileHandler::getProcessString() const
 {
+    std::stringstream buf;
     ImageLayer* imageLayer = dynamic_cast< ImageLayer* >( _layer.get() );
     ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer* >( _layer.get() );    
 
-    std::stringstream buf;
-    buf << "osgearth_cache --seed ";
-    if (imageLayer)
-    {        
-        for (int i = 0; i < _map->getNumImageLayers(); i++)
+    unsigned index = _map->getIndexOfLayer(_layer.get());
+    if (index < _map->getNumLayers())
+    {
+        buf << "osgearth_cache --seed ";
+        if (imageLayer)
         {
-            if (imageLayer == _map->getImageLayerAt(i))
-            {
-                buf << " --image " << i << " ";
-                break;
-            }
+            buf << " --image " << index << " ";
         }
-    }
-    else if (elevationLayer)
-    {
-        for (int i = 0; i < _map->getNumElevationLayers(); i++)
+        else if (elevationLayer)
         {
-            if (elevationLayer == _map->getElevationLayerAt(i))
-            {
-                buf << " --elevation " << i << " ";
-                break;
-            }
+            buf << " --elevation " << index << " ";
         }
     }
     return buf.str();
@@ -131,7 +119,7 @@ void CacheSeed::setVisitor(TileVisitor* visitor)
     _visitor = visitor;
 }
 
-void CacheSeed::run( TerrainLayer* layer, Map* map )
+void CacheSeed::run( TerrainLayer* layer, const Map* map )
 {
     _visitor->setTileHandler( new CacheTileHandler( layer, map ) );
     _visitor->run( map->getProfile() );
diff --git a/src/osgEarth/Capabilities b/src/osgEarth/Capabilities
index b3bf905..bc0ea1e 100644
--- a/src/osgEarth/Capabilities
+++ b/src/osgEarth/Capabilities
@@ -33,7 +33,7 @@ namespace osgEarth
 
     /**
      * Stores information about the hardware and graphics system capbilities.
-     * The osgEarth::Registry stores a singleton Capabilities object that you can 
+     * The osgEarth::Registry stores a singleton Capabilities object that you can
      * use to determine what your system supports.
      */
     class OSGEARTH_EXPORT Capabilities : public osg::Referenced
@@ -64,13 +64,13 @@ namespace osgEarth
         int getDepthBufferBits() const { return _depthBits; }
 
         /** whether the GPU supports shaders. */
-        bool supportsGLSL() const { 
+        bool supportsGLSL() const {
             return _supportsGLSL; }
-        
+
         /** whether the GPU supports a minimum GLSL version */
-        bool supportsGLSL(float minimumVersion) const { 
+        bool supportsGLSL(float minimumVersion) const {
             return _supportsGLSL && _GLSLversion >= minimumVersion; }
-        
+
         /** whether the GPU supports a minimum GLSL version (as an int; e.g. 1.2 => "120") */
         bool supportsGLSL(unsigned minVersionInt) const {
             return _supportsGLSL && ((unsigned)(_GLSLversion*100.0f)) >= minVersionInt; }
@@ -79,7 +79,7 @@ namespace osgEarth
         float getGLSLVersion() const { return _GLSLversion; }
 
         /** GLSL version as an integer x 100. I.e.: GLSL 1.4 => 140. */
-        unsigned getGLSLVersionInt() const { return (unsigned)(_GLSLversion*100.0f); }
+        unsigned getGLSLVersionInt() const { return (unsigned)(_GLSLversion*100.0f + 0.5f); }
 
         /** Are we running OpenGLES? */
         bool isGLES() const { return _isGLES; }
@@ -140,7 +140,7 @@ namespace osgEarth
 
         /** whether the GPU supports writing to the depth fragment */
         bool supportsFragDepthWrite() const { return _supportsFragDepthWrite; }
-        
+
         /** whether the GPU supports a texture compression scheme */
         bool supportsTextureCompression(const osg::Texture::InternalFormatMode& mode) const;
 
diff --git a/src/osgEarth/Capabilities.cpp b/src/osgEarth/Capabilities.cpp
index 5f48302..c258a94 100644
--- a/src/osgEarth/Capabilities.cpp
+++ b/src/osgEarth/Capabilities.cpp
@@ -17,6 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarth/Capabilities>
+#include <osgEarth/Version>
 #include <osg/FragmentProgram>
 #include <osg/GraphicsContext>
 #include <osg/GL>
@@ -55,6 +56,8 @@ struct MyGraphicsContext
         traits->doubleBuffer = false;
         traits->sharedContext = 0;
         traits->pbuffer = false;
+        traits->glContextVersion = osg::DisplaySettings::instance()->getGLContextVersion();
+        traits->glContextProfileMask = osg::DisplaySettings::instance()->getGLContextProfileMask();
 
         // Intel graphics adapters dont' support pbuffers, and some of their drivers crash when
         // you try to create them. So by default we will only use the unmapped/pbuffer method
@@ -151,7 +154,7 @@ _maxTextureBufferSize   ( 0 )
     _numProcessors = OpenThreads::GetNumberOfProcessors();
 
     // GLES compile?
-#if (defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE))
+#if (defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE) || defined(OSG_GLES3_AVAILABLE))
     _isGLES = true;
 #else
     _isGLES = false;
@@ -165,6 +168,8 @@ _maxTextureBufferSize   ( 0 )
         osg::GraphicsContext* gc = mgc._gc.get();
         unsigned int id = gc->getState()->getContextID();
         const osg::GL2Extensions* GL2 = osg::GL2Extensions::Get( id, true );
+
+        OE_INFO << LC << "osgEarth Version: " << osgEarthGetVersion() << std::endl;
         
         if ( ::getenv("OSGEARTH_NO_GLSL") )
         {
@@ -173,11 +178,7 @@ _maxTextureBufferSize   ( 0 )
         }
         else
         {
-#if OSG_MIN_VERSION_REQUIRED(3,3,3)
             _supportsGLSL = GL2->isGlslSupported;
-#else
-			_supportsGLSL = GL2->isGlslSupported();
-#endif
         }
 
         OE_INFO << LC << "Detected hardware capabilities:" << std::endl;
@@ -191,24 +192,31 @@ _maxTextureBufferSize   ( 0 )
         _version = std::string( reinterpret_cast<const char*>(glGetString(GL_VERSION)) );
         OE_INFO << LC << "  Version = " << _version << std::endl;
 
+#if !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)
         glGetIntegerv( GL_MAX_TEXTURE_UNITS, &_maxFFPTextureUnits );
         //OE_INFO << LC << "  Max FFP texture units = " << _maxFFPTextureUnits << std::endl;
+#endif
 
         glGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &_maxGPUTextureUnits );
         OE_INFO << LC << "  Max GPU texture units = " << _maxGPUTextureUnits << std::endl;
 
+#if !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)
         glGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &_maxGPUTextureCoordSets );
+#else
+        _maxGPUTextureCoordSets = _maxGPUTextureUnits;
+#endif
         OE_INFO << LC << "  Max GPU texture coord indices = " << _maxGPUTextureCoordSets << std::endl;
 
         glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &_maxGPUAttribs );
         OE_INFO << LC << "  Max GPU attributes = " << _maxGPUAttribs << std::endl;
 
+#if !(defined(OSG_GL3_AVAILABLE))
         glGetIntegerv( GL_DEPTH_BITS, &_depthBits );
         OE_INFO << LC << "  Depth buffer bits = " << _depthBits << std::endl;
-
+#endif
         
         glGetIntegerv( GL_MAX_TEXTURE_SIZE, &_maxTextureSize );
-#if !(defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE))
+#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)
         // Use the texture-proxy method to determine the maximum texture size 
         for( int s = _maxTextureSize; s > 2; s >>= 1 )
         {
@@ -224,7 +232,6 @@ _maxTextureBufferSize   ( 0 )
 #endif
         OE_INFO << LC << "  Max texture size = " << _maxTextureSize << std::endl;
 
-        //PORT at tom, what effect will this have?
 #ifdef OSG_GL_FIXED_FUNCTION_AVAILABLE
         glGetIntegerv( GL_MAX_LIGHTS, &_maxLights );
 #else
@@ -236,11 +243,7 @@ _maxTextureBufferSize   ( 0 )
 
         if ( _supportsGLSL )
         {
-#if OSG_MIN_VERSION_REQUIRED(3,3,3)
 			_GLSLversion = GL2->glslLanguageVersion;
-#else
-            _GLSLversion = GL2->getLanguageVersion();
-#endif
             OE_INFO << LC << "  GLSL Version = " << getGLSLVersionInt() << std::endl;
         }
 
@@ -291,13 +294,22 @@ _maxTextureBufferSize   ( 0 )
             _supportsUniformBufferObjects = false;
         }
 
+#if !defined(OSG_GLES3_AVAILABLE)
         _supportsNonPowerOfTwoTextures =
             osg::isGLExtensionSupported( id, "GL_ARB_texture_non_power_of_two" );
+#else
+        _supportsNonPowerOfTwoTextures = true;
+#endif
         OE_INFO << LC << "  NPOT textures = " << SAYBOOL(_supportsNonPowerOfTwoTextures) << std::endl;
 
+
+#if !defined(OSG_GLES3_AVAILABLE)
         _supportsTextureBuffer = 
             osg::isGLExtensionOrVersionSupported( id, "GL_ARB_texture_buffer_object", 3.0 ) ||
             osg::isGLExtensionOrVersionSupported( id, "GL_EXT_texture_buffer_object", 3.0 );
+#else
+        _supportsTextureBuffer = false;
+#endif
 
         if ( _supportsTextureBuffer )
         {
@@ -315,16 +327,13 @@ _maxTextureBufferSize   ( 0 )
         OE_INFO << LC << "  Transform feedback = " << SAYBOOL(supportsTransformFeedback) << "\n";
 
 
-        // Writing to gl_FragDepth is not supported under GLES:
+        // Writing to gl_FragDepth is not supported under GLES, is supported under gles3
 #if (defined(OSG_GLES1_AVAILABLE) || defined(OSG_GLES2_AVAILABLE))
         _supportsFragDepthWrite = false;
 #else
         _supportsFragDepthWrite = true;
 #endif
 
-        //_supportsTexture2DLod = osg::isGLExtensionSupported( id, "GL_ARB_shader_texture_lod" );
-        //OE_INFO << LC << "  texture2DLod = " << SAYBOOL(_supportsTexture2DLod) << std::endl;
-
         // NVIDIA:
         bool isNVIDIA = _vendor.find("NVIDIA") == 0;
 
diff --git a/src/osgEarth/ClampableNode b/src/osgEarth/ClampableNode
index f093dbe..cdd6ca2 100644
--- a/src/osgEarth/ClampableNode
+++ b/src/osgEarth/ClampableNode
@@ -21,58 +21,33 @@
 #define OSGEARTH_CLAMPABLE_NODE_H 1
 
 #include <osgEarth/Common>
-#include <osgEarth/DepthOffset>
-#include <osgEarth/OverlayNode>
 #include <osg/Group>
+#include <osg/observer_ptr>
 
 namespace osgEarth
 {
     class MapNode;
 
     /**
-     * Node graph that can be "clamped" on the MapNode terrain
-     * using the overlay decorator.
-     *
-     * Usage: Create this node and put it anywhere in the scene graph. The
-     * subgraph of this node will be draped on the MapNode's terrain.
+     * Node graph that will be GPU-clamped on the terrain, if supported.
+     * This group must be a descendant of a MapNode.
      */
-    class OSGEARTH_EXPORT ClampableNode : public OverlayNode, public DepthOffsetInterface
+    class OSGEARTH_EXPORT ClampableNode : public osg::Group
     {
     public:
-        /**
-         * Constructs a new clampable node.
-         */
-        ClampableNode( MapNode* mapNode, bool clamped =true );
-
-
-    public: // DepthOffsetInterface
-
-        /** Sets the depth offsetting options. See DepthOffset */
-        void setDepthOffsetOptions( const DepthOffsetOptions& options );
-
-        /** Gets the depth offsetting options. See DepthOffset */
-        const DepthOffsetOptions& getDepthOffsetOptions() const;
+        //! Constructs a new clampable node.
+        ClampableNode();
 
     public: // osg::Node
 
-        virtual osg::BoundingSphere computeBound() const;
-
         virtual void traverse(osg::NodeVisitor& nv);
 
-    public: // backwards-compatibility
-
-        /** @deprecated */
-        void setClamped( bool value ) { setActive(value); }
-        /** @deprecated */
-        bool getClamped() const { return getActive(); }
-
     protected:
         void setUniforms();
         void dirty();
-        void scheduleUpdate();
 
-        DepthOffsetAdapter _adapter;
-        bool               _updatePending;
+        bool _mapNodeUpdateRequested;
+        osg::observer_ptr<MapNode> _mapNode;
 
         virtual ~ClampableNode() { }
     };
diff --git a/src/osgEarth/ClampableNode.cpp b/src/osgEarth/ClampableNode.cpp
index a9981bc..3df99d5 100644
--- a/src/osgEarth/ClampableNode.cpp
+++ b/src/osgEarth/ClampableNode.cpp
@@ -22,76 +22,116 @@
 #include <osgEarth/DepthOffset>
 #include <osgEarth/OverlayDecorator>
 #include <osgEarth/MapNode>
+#include <osgEarth/NodeUtils>
 
 #define LC "[ClampableNode] "
 
 using namespace osgEarth;
 
-//------------------------------------------------------------------------
 
-namespace
-{
-    static osg::Group* getTechniqueGroup(MapNode* m)
-    {
-        return m ? m->getOverlayDecorator()->getGroup<ClampingTechnique>() : 0L;
-    }
-}
-
-//------------------------------------------------------------------------
-
-ClampableNode::ClampableNode( MapNode* mapNode, bool active ) :
-OverlayNode( mapNode, active, &getTechniqueGroup ),
-_updatePending( false )
+ClampableNode::ClampableNode() :
+_mapNodeUpdateRequested(true)
 {
-    _adapter.setGraph( this );
+    // bounding box culling doesn't work on clampable geometry
+    // since the GPU will be moving verts. So, disable the default culling
+    // for this node so we can out own culling in traverse().
+    setCullingActive(false);
 
-    if ( _adapter.isDirty() )
-        _adapter.recalculate();
+    // for the mapnode update:
+    ADJUST_UPDATE_TRAV_COUNT(this, +1);
 }
 
 void
-ClampableNode::setDepthOffsetOptions(const DepthOffsetOptions& options)
+ClampableNode::traverse(osg::NodeVisitor& nv)
 {
-    _adapter.setDepthOffsetOptions(options);
-    if ( _adapter.isDirty() && !_updatePending )
-        scheduleUpdate();
-}
+    if ( nv.getVisitorType() == nv.CULL_VISITOR )
+    {
+        // Lock a reference to the map node:
+        osg::ref_ptr<MapNode> mapNode;
+        if (_mapNode.lock(mapNode))
+        {
+            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
 
-const DepthOffsetOptions&
-ClampableNode::getDepthOffsetOptions() const
-{
-    return _adapter.getDepthOffsetOptions();
-}
+            // Custom culling. Since clamped geometry can start far outside of the 
+            // view frustum, normal culling won't work. Instead, project the
+            // bounding sphere upwards (along its up vector) on to the center plane
+            // of the view frustum. Then cull it based on the new simulated location.
+            osg::RefMatrix* MV = cv->getModelViewMatrix();
+            osg::Matrix MVinverse;
+            MVinverse.invert(*MV);
 
-void
-ClampableNode::scheduleUpdate()
-{
-    if ( !_updatePending && getDepthOffsetOptions().enabled() == true )
-    {
-        ADJUST_UPDATE_TRAV_COUNT(this, 1);
-        _updatePending = true;
+            // Actual bounds of geometry:
+            osg::BoundingSphere bs = getBound();
+
+            // Find any two points on the bounding sphere's up-vector
+            // and transform them into view space:
+            osg::Vec3d p0 = bs.center() * (*MV);
+            osg::Vec3d p1 =
+                mapNode->isGeocentric() ? (bs.center() * 2.0 * (*MV)) :
+                                          (bs.center() + osg::Vec3d(0, 0, bs.radius())) * (*MV);
+
+            // Center plane of the view frustum (in view space)
+            static osg::Vec3d v0(0, 0, 0);  // point on the plane
+            static osg::Vec3d n(0, 1, 0);   // normal vector to the plane
+
+            // Find the intersection of the up vector and the center plane
+            // and then transform the result back into world space for culling.
+            osg::Vec3d w = p0 - v0;
+            osg::Vec3d u = p1 - p0;
+            double t = (-n * w) / (n * u);
+            bs.center() = (p0 + u*t) * MVinverse;
+
+            if (cv->isCulled(bs) == false)
+            {
+                // Passed the cull test, so put this node in the clamping cull set.
+                ClampingCullSet& cullSet = mapNode->getClampingManager()->get( cv->getCurrentCamera() );
+                cullSet.push( this, cv->getNodePath(), nv.getFrameStamp() );
+            }
+        }
     }
-}
 
-osg::BoundingSphere
-ClampableNode::computeBound() const
-{
-    static Threading::Mutex s_mutex;
+    else if (nv.getVisitorType() == nv.UPDATE_VISITOR)
+    {        
+        if (_mapNodeUpdateRequested)
+        {
+            if (_mapNode.valid() == false)
+            {
+                _mapNode = osgEarth::findInNodePath<MapNode>(nv);
+            }
+
+            if (_mapNode.valid())
+            {
+                _mapNodeUpdateRequested = false;
+                ADJUST_UPDATE_TRAV_COUNT(this, -1);
+            }
+        }
+
+        osg::Group::traverse(nv);
+    }
+    else
     {
-        Threading::ScopedMutexLock lock(s_mutex);
-        const_cast<ClampableNode*>(this)->scheduleUpdate();
+        osg::Group::traverse(nv);
     }
-    return OverlayNode::computeBound();
 }
 
-void
-ClampableNode::traverse(osg::NodeVisitor& nv)
+
+//...........................................................................
+
+#undef  LC
+#define LC "[ClampableNode Serializer] "
+
+#include <osgDB/ObjectWrapper>
+#include <osgDB/InputStream>
+#include <osgDB/OutputStream>
+
+namespace
 {
-    if ( _updatePending && nv.getVisitorType() == nv.UPDATE_VISITOR )
+    REGISTER_OBJECT_WRAPPER(
+        ClampableNode,
+        new osgEarth::ClampableNode,
+        osgEarth::ClampableNode,
+        "osg::Object osg::Node osg::Group osgEarth::ClampableNode")
     {
-        _adapter.recalculate();
-        ADJUST_UPDATE_TRAV_COUNT( this, -1 );
-        _updatePending = false;
+        //nop
     }
-    OverlayNode::traverse( nv );
 }
diff --git a/src/osgEarth/Clamping b/src/osgEarth/Clamping
index d45d1c5..a75afd5 100644
--- a/src/osgEarth/Clamping
+++ b/src/osgEarth/Clamping
@@ -23,8 +23,12 @@
 #define OSGEARTH_CLAMPING_H
 
 #include <osgEarth/Common>
+#include <osgEarth/Containers>
+#include <osgEarth/ClampableNode>
 #include <osg/Node>
 #include <osg/Geometry>
+#include <osg/ObserverNodePath>
+#include <osg/Camera>
 
 namespace osgEarth
 {
@@ -40,9 +44,15 @@ namespace osgEarth
      */
     class OSGEARTH_EXPORT Clamping
     {
-    public:
+    public: // mutable
+
         // GLSL attribute binding location for clamping vertex attributes
-        static const int AnchorAttrLocation;
+        static int AnchorAttrLocation;
+
+        // GLSL attribute binding location for clamping vertex heights
+        static int HeightsAttrLocation;
+
+    public: // non-mutable
 
         // Name of the clamping anchor vertex attribute
         static const char* AnchorAttrName;
@@ -54,9 +64,6 @@ namespace osgEarth
         // Name of the uniform used to apply an altitude offset to clamped geometry
         static const char* AltitudeOffsetUniformName;
 
-        // GLSL attribute binding location for clamping vertex heights
-        static const int HeightsAttrLocation;
-
         // Name of the clamping heights array attribute
         static const char* HeightsAttrName;
 
@@ -66,6 +73,8 @@ namespace osgEarth
         static const float ClampToAnchor;
         static const float ClampToGround;
 
+    public: // utility methods
+
         // Installs a uniform on the stateset indicating that geometry has the
         // clamping vertex attribute installed.
         static void installHasAttrsUniform(
@@ -83,6 +92,60 @@ namespace osgEarth
             osg::Geometry*   geom,
             osg::FloatArray* heights );
     };
+    
+
+    /**
+     * Culling set for tracking groups whose contents should be GPU-clamped
+     * The ClampableNode class inserts scene graphs into this objects during
+     * the CULL traversal; then the ClampingTechnique traverses those graphs.
+     */
+    class OSGEARTH_INTERNAL ClampingCullSet
+    {
+    public:
+        struct Entry
+        {            
+            osg::ref_ptr<osg::Group>     _node;
+            osg::ref_ptr<osg::RefMatrix> _matrix;
+            osg::ObserverNodePath        _path;
+            int                          _frame;
+        };
+
+    public:
+        ClampingCullSet();
+        ~ClampingCullSet() { } // not virtual
+
+        /** Pushes a node and its matrix into the cull set */
+        void push(ClampableNode* node, const osg::NodePath& path, const osg::FrameStamp* stamp);
+
+        /** Runs a node visitor on the cull set, optionally popping as it goes along. */
+        void accept(osg::NodeVisitor& nv);
+
+        /** Bounds of this set */
+        const osg::BoundingSphere& getBound() const { return _bs; }
+
+        /** Number of elements in the set */
+        unsigned size() const { return _entries.size(); }
+
+    private:
+        std::vector<Entry>  _entries;
+        osg::BoundingSphere _bs;
+        bool                _frameCulled;
+    };
+
+    /**
+     * Houses all the active ClampingCullSets under an osgEarth TerrainEngine.
+     * ClampableNode will use this object to gain access to a ClampingCullSet
+     * during the CULL traversal.
+     */
+    class OSGEARTH_INTERNAL ClampingManager
+    {
+    public:
+        //! Gets the cull set associated with a camera.
+        ClampingCullSet& get(const osg::Camera*);
+
+    private:
+        PerObjectFastMap<const osg::Camera*, ClampingCullSet> _sets;
+    };
 }
 
 #endif // OSGEARTH_CLAMPING_H
diff --git a/src/osgEarth/Clamping.cpp b/src/osgEarth/Clamping.cpp
index a89535c..7d29a87 100644
--- a/src/osgEarth/Clamping.cpp
+++ b/src/osgEarth/Clamping.cpp
@@ -20,19 +20,23 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
 #include <osgEarth/Clamping>
+#include <osgEarth/CullingUtils>
+#include <osgEarth/Registry>
 #include <osg/Drawable>
 #include <osg/NodeVisitor>
 #include <osg/Geode>
 #include <osg/Geometry>
+#include <osgUtil/CullVisitor>
 
 using namespace osgEarth;
 
-const int   Clamping::AnchorAttrLocation        = osg::Drawable::ATTRIBUTE_6;
+int         Clamping::AnchorAttrLocation        = osg::Drawable::ATTRIBUTE_6;
+int         Clamping::HeightsAttrLocation       = osg::Drawable::FOG_COORDS;
+
 const char* Clamping::AnchorAttrName            = "oe_clamp_attrs";
 const char* Clamping::HasAttrsUniformName       = "oe_clamp_hasAttrs";
 const char* Clamping::AltitudeOffsetUniformName = "oe_clamp_altitudeOffset";
 
-const int   Clamping::HeightsAttrLocation       = osg::Drawable::FOG_COORDS;
 const char* Clamping::HeightsAttrName           = "oe_clamp_height";
 
 const float Clamping::ClampToAnchor = 1.0f;
@@ -72,12 +76,10 @@ namespace
             }
         }
 
-        void apply(osg::Geode& geode) 
+        void apply(osg::Drawable& drawable)
         {
-            for(unsigned i=0; i<geode.getNumDrawables(); ++i)
-            {
-                apply( geode.getDrawable(i)->asGeometry() );
-            }
+            osg::Geometry* geom = drawable.asGeometry();
+            if (geom) apply(geom);
         }
     };
 }
@@ -108,7 +110,8 @@ Clamping::installHasAttrsUniform(osg::StateSet* stateset)
 {
     if ( stateset )
     {
-        stateset->addUniform( new osg::Uniform(Clamping::HasAttrsUniformName, true) );
+        stateset->setDefine("OE_CLAMP_HAS_ATTRIBUTES");
+        //stateset->addUniform( new osg::Uniform(Clamping::HasAttrsUniformName, true) );
     }
 }
 
@@ -122,3 +125,132 @@ Clamping::setHeights(osg::Geometry* geom, osg::FloatArray* hats)
         geom->setVertexAttribNormalize( HeightsAttrLocation, false );
     }
 }
+
+
+
+#undef LC
+#define LC "[ClampingCullSet] "
+
+ClampingCullSet::ClampingCullSet() :
+_frameCulled( true )
+{
+    // nop
+}
+
+void
+ClampingCullSet::push(ClampableNode* node, const osg::NodePath& path, const osg::FrameStamp* fs)
+{
+    // Reset the set if this is the first push after a cull.
+    if ( _frameCulled )
+    {
+        _frameCulled = false;
+        _entries.clear();
+        _bs.init();
+    }
+
+    _entries.push_back( Entry() );
+    Entry& entry = _entries.back();
+    entry._node = node;
+    entry._path.setNodePath( path );
+    entry._matrix = new osg::RefMatrix( osg::computeLocalToWorld(path) );
+    entry._frame = fs ? fs->getFrameNumber() : 0;
+    _bs.expandBy( osg::BoundingSphere(
+        node->getBound().center() * (*entry._matrix.get()),
+        node->getBound().radius() ));
+
+    OE_DEBUG << LC << "Pushed " << node << " on frame " << entry._frame << std::endl;
+}
+
+void
+ClampingCullSet::accept(osg::NodeVisitor& nv)
+{
+    if ( nv.getVisitorType() == nv.CULL_VISITOR )
+    {
+        ProxyCullVisitor* cv = dynamic_cast<ProxyCullVisitor*>(&nv);
+
+        // We will use the visitor's path to prevent doubely-applying the statesets
+        // of common ancestors
+        const osg::NodePath& nvPath = nv.getNodePath();
+
+        int frame = nv.getFrameStamp() ? nv.getFrameStamp()->getFrameNumber() : 0u;
+
+        unsigned passed = 0u;
+
+        for( std::vector<Entry>::iterator entry = _entries.begin(); entry != _entries.end(); ++entry )
+        {
+            if ( frame - entry->_frame > 1 )
+                continue;
+
+            // If there's an active (non-identity matrix), apply it
+            if ( entry->_matrix.valid() )
+            {
+                entry->_matrix->postMult( *cv->getModelViewMatrix() );
+                cv->pushModelViewMatrix( entry->_matrix.get(), osg::Transform::RELATIVE_RF );
+            }
+
+            // After pushing the matrix, we can perform the culling bounds test.
+            if (!cv->isCulledByProxyFrustum(*entry->_node.get()))
+            {
+                // Apply the statesets in the entry's node path, but skip over the ones that are
+                // shared with the current visitor's path since they are already in effect.
+                // Count them so we can pop them later.
+                int numStateSets = 0;
+                osg::RefNodePath nodePath;
+                if ( entry->_path.getRefNodePath(nodePath) )
+                {
+                    for(unsigned i=0; i<nodePath.size(); ++i)
+                    {
+                        if (nodePath[i].valid())
+                        {
+                            if (i >= nvPath.size() || nvPath[i] != nodePath[i].get())
+                            {
+                                osg::StateSet* stateSet = nodePath[i]->getStateSet();
+                                if ( stateSet )
+                                {
+                                    cv->getCullVisitor()->pushStateSet( stateSet );
+                                    ++numStateSets;
+                                }
+                            }
+                        }
+                    }
+                }
+
+                // Cull the DrapeableNode's children (but not the DrapeableNode itself!)
+                for(unsigned i=0; i<entry->_node->getNumChildren(); ++i)
+                {
+                    entry->_node->getChild(i)->accept( nv );
+                }
+            
+                // pop the same number we pushed
+                for(int i=0; i<numStateSets; ++i)
+                {
+                    cv->getCullVisitor()->popStateSet();
+                }
+
+                ++passed;
+            }
+
+            // pop the model view:
+            if ( entry->_matrix.valid() )
+            {
+                cv->popModelViewMatrix();
+            }
+            
+            //Registry::instance()->startActivity("ClampingCullSet", Stringify() << std::hex << this << std::dec << " / " << passed << "/" << _entries.size());
+        }
+
+        // mark this set so it will reset for the next frame
+        _frameCulled = true;
+    }
+}
+
+ClampingCullSet&
+ClampingManager::get(const osg::Camera* cam)
+{
+    // Known issue: it is possible for a draping cull set to be "orphaned" - this
+    // would happen if the cull set were populated and then not used. This is a
+    // very unlikely scenario (because the scene graph would have to change mid-cull)
+    // but nevertheless possible.
+    //Registry::instance()->startActivity("ClampingManager", Stringify() << _sets.size());
+    return _sets.get(cam);
+}
\ No newline at end of file
diff --git a/src/osgEarth/ClampingTechnique b/src/osgEarth/ClampingTechnique
index 6be9095..1889975 100644
--- a/src/osgEarth/ClampingTechnique
+++ b/src/osgEarth/ClampingTechnique
@@ -24,8 +24,9 @@
 
 #include <osgEarth/Common>
 #include <osgEarth/OverlayDecorator>
+#include <osgEarth/Clamping>
 
-#define OSGEARTH_CLAMPING_BIN           "osgEarth::ClampingBin"
+#define OSGEARTH_CLAMPING_BIN "osgEarth::ClampingBin"
 
 namespace osgEarth
 {
@@ -93,6 +94,9 @@ namespace osgEarth
         void onInstall( TerrainEngineNode* engine );
 
         void onUninstall( TerrainEngineNode* engine );
+        
+        const osg::BoundingSphere& getBound(
+            OverlayDecorator::TechRTTParams& params) const;
 
     protected:
         virtual ~ClampingTechnique() { }
@@ -102,6 +106,10 @@ namespace osgEarth
         optional<int>      _textureSize;
         TerrainEngineNode* _engine;
 
+        mutable ClampingManager _clampingManager;
+        ClampingManager& getClampingManager() { return _clampingManager; }
+        friend class MapNode;
+
     private:
         void setUpCamera(OverlayDecorator::TechRTTParams& params);
     };
diff --git a/src/osgEarth/ClampingTechnique.cpp b/src/osgEarth/ClampingTechnique.cpp
index 25116d7..f5b9735 100644
--- a/src/osgEarth/ClampingTechnique.cpp
+++ b/src/osgEarth/ClampingTechnique.cpp
@@ -36,16 +36,11 @@
 #include <osg/ValueObject>
 #include <osg/Timer>
 
-#include <osgDB/WriteFile>
-
 #define LC "[ClampingTechnique] "
 
 //#define OE_TEST if (_dumpRequested) OE_INFO << std::setprecision(9)
 #define OE_TEST OE_NULL
 
-//#define USE_RENDER_BIN 1
-#undef USE_RENDER_BIN
-
 //#define DUMP_RTT_IMAGE 1
 //#undef DUMP_RTT_IMAGE
 
@@ -55,14 +50,9 @@ using namespace osgEarth;
 
 //---------------------------------------------------------------------------
 
+#ifdef TIME_RTT_CAMERA
 namespace
 {
-    osg::Group* s_providerImpl(MapNode* mapNode)
-    {
-        return mapNode ? mapNode->getOverlayDecorator()->getGroup<ClampingTechnique>() : 0L;
-    }
-
-#ifdef TIME_RTT_CAMERA
     static osg::Timer_t t0, t1;
     struct RttIn : public osg::Camera::DrawCallback {
         void operator()(osg::RenderInfo& r) const {
@@ -75,10 +65,8 @@ namespace
             OE_NOTICE << "RTT = " << osg::Timer::instance()->delta_m(t0, t1) << "ms" << std::endl;
         }
     };
-#endif
 }
-
-ClampingTechnique::TechniqueProvider ClampingTechnique::Provider = s_providerImpl;
+#endif
 
 //---------------------------------------------------------------------------
 
@@ -120,79 +108,6 @@ namespace
 #endif
 }
 
-#ifdef USE_RENDER_BIN
-
-//---------------------------------------------------------------------------
-// Custom bin for clamping.
-
-namespace
-{
-    struct ClampingRenderBin : public osgUtil::RenderBin
-    {
-        struct PerViewData // : public osg::Referenced
-        {
-            osg::observer_ptr<LocalPerViewData> _techData;
-        };
-
-        // shared across ALL render bin instances.
-        typedef Threading::PerObjectMap<osg::Camera*, PerViewData> PerViewDataMap;
-        PerViewDataMap* _pvd; 
-
-        // support cloning (from RenderBin):
-        virtual osg::Object* cloneType() const { return new ClampingRenderBin(); }
-        virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new ClampingRenderBin(*this,copyop); } // note only implements a clone of type.
-        virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast<const ClampingRenderBin*>(obj)!=0L; }
-        virtual const char* libraryName() const { return "osgEarth"; }
-        virtual const char* className() const { return "ClampingRenderBin"; }
-
-
-        // constructs the prototype for this render bin.
-        ClampingRenderBin() : osgUtil::RenderBin()
-        {
-            this->setName( OSGEARTH_CLAMPING_BIN );
-            _pvd = new PerViewDataMap();
-        }
-
-        ClampingRenderBin( const ClampingRenderBin& rhs, const osg::CopyOp& op )
-            : osgUtil::RenderBin( rhs, op ), _pvd( rhs._pvd )
-        {
-            // zero out the stateset...dont' want to share that!
-            _stateset = 0L;
-        }
-
-        // override.
-        void sortImplementation()
-        {
-            copyLeavesFromStateGraphListToRenderLeafList();
-        }
-
-        // override.
-        void drawImplementation(osg::RenderInfo& renderInfo, osgUtil::RenderLeaf*& previous)
-        {
-            // find and initialize the state set for this camera.
-            if ( !_stateset.valid() )
-            {
-                osg::Camera* camera = renderInfo.getCurrentCamera();
-                PerViewData& data = _pvd->get(camera);
-                if ( data._techData.valid() )
-                {
-                    _stateset = data._techData->_groupStateSet.get();
-                    LocalPerViewData* local = static_cast<LocalPerViewData*>(data._techData.get());
-                    local->_renderLeafCount = _renderLeafList.size();
-                }
-            }
-
-            osgUtil::RenderBin::drawImplementation( renderInfo, previous );
-        }
-    };
-}
-
-/** the static registration. */
-extern "C" void osgEarth_clamping_bin_registration(void) {}
-static osgEarthRegisterRenderBinProxy<ClampingRenderBin> s_regbin(OSGEARTH_CLAMPING_BIN);
-
-#endif // USE_RENDER_BIN
-
 //---------------------------------------------------------------------------
 
 ClampingTechnique::ClampingTechnique() :
@@ -210,12 +125,7 @@ _engine(0L)
 bool
 ClampingTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
 {
-#ifdef USE_RENDER_BIN
-    //TODO: reconsider
-    return true;
-#else
-    return params._group->getNumChildren() > 0;
-#endif
+    return getBound(params).valid();
 }
 
 
@@ -228,6 +138,8 @@ ClampingTechnique::reestablish(TerrainEngineNode* engine)
 void
 ClampingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
 {
+    OE_INFO << LC << "Using texture size = " << _textureSize.get() << std::endl;
+
     // To store technique-specific per-view info:
     LocalPerViewData* local = new LocalPerViewData();
     params._techniqueData = local;
@@ -246,8 +158,9 @@ ClampingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
     local->_rttTexture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE );
     //local->_rttTexture->setBorderColor( osg::Vec4(0,0,0,1) );
 
-    // set up the RTT camera:
+    // set up the RTT camera for rendering a depth map of the terrain:
     params._rttCamera = new osg::Camera();
+    params._rttCamera->setName("GPU Clamping");
     params._rttCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT );
     params._rttCamera->setClearDepth( 1.0 );
     params._rttCamera->setClearMask( GL_DEPTH_BUFFER_BIT );
@@ -312,6 +225,9 @@ ClampingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
 
     local->_groupStateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
 
+    // set a define so the shaders know we are running GPU clamping.
+    local->_groupStateSet->setDefine("OE_GPU_CLAMPING");
+
     // uniform for the horizon distance (== max clamping distance)
     local->_horizonDistance2Uniform = local->_groupStateSet->getOrCreateUniform(
         "oe_clamp_horizonDistance2",
@@ -376,29 +292,15 @@ ClampingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
     if ( !params._rttCamera.valid() && hasData(params) )
     {
         setUpCamera( params );
-
-#ifdef USE_RENDER_BIN
-
-        // store our camera's stateset in the perview data.
-        ClampingRenderBin* bin = dynamic_cast<ClampingRenderBin*>( osgUtil::RenderBin::getRenderBinPrototype(OSGEARTH_CLAMPING_BIN) );
-        if ( bin )
-        {
-            ClampingRenderBin::PerViewData& data = bin->_pvd->get( cv->getCurrentCamera() );
-            LocalPerViewData* local = static_cast<LocalPerViewData*>(params._techniqueData.get());
-            data._techData = local;
-        }
-        else
-        {
-            OE_WARN << LC << "Odd, no prototype found for the clamping bin." << std::endl;
-        }
-
-#endif
     }
-
-#ifdef TIME_RTT_CAMERA
-#endif
 }
 
+       
+const osg::BoundingSphere&
+ClampingTechnique::getBound(OverlayDecorator::TechRTTParams& params) const
+{
+    return _clampingManager.get(params._mainCamera).getBound();
+}
 
 void
 ClampingTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
@@ -461,24 +363,21 @@ ClampingTechnique::cullOverlayGroup(OverlayDecorator::TechRTTParams& params,
         local._depthClipToCamViewUniform->set( depthClipToCameraView );
 #endif
 
-        if ( params._group->getNumChildren() > 0 )
-        {
-            // traverse the overlay nodes, applying the clamping shader.
-            cv->pushStateSet( local._groupStateSet.get() );
+        // traverse the overlay nodes, applying the clamping shader.
+        cv->pushStateSet(local._groupStateSet.get());
 
-            // Since the vertex shader is moving the verts to clamp them to the terrain,
-            // OSG will not be able to properly cull the geometry. (Specifically: OSG may
-            // cull geometry which is invisible when NOT clamped, but becomes visible after
-            // GPU clamping.) We work around that by using a Proxy cull visitor that will 
-            // use the RTT camera's matrixes for frustum culling (instead of the main camera's).
-            ProxyCullVisitor pcv( cv, params._rttProjMatrix, params._rttViewMatrix );
+        // Since the vertex shader is moving the verts to clamp them to the terrain,
+        // OSG will not be able to properly cull the geometry. (Specifically: OSG may
+        // cull geometry which is invisible when NOT clamped, but becomes visible after
+        // GPU clamping.) We work around that by using a Proxy cull visitor that will 
+        // use the RTT camera's matrixes for frustum culling (instead of the main camera's).
+        ProxyCullVisitor pcv(cv, params._rttProjMatrix, params._rttViewMatrix);
 
-            // cull the clampable geometry.
-            params._group->accept( pcv );
+        // cull the clampable geometry.
+        getClampingManager().get(cv->getCurrentCamera()).accept(pcv);
 
-            // done; pop the clamping shaders.
-            cv->popStateSet();
-        }
+        // done; pop the clamping shaders.
+        cv->popStateSet();
     }
 }
 
@@ -503,8 +402,6 @@ ClampingTechnique::onInstall( TerrainEngineNode* engine )
     {
         unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
         _textureSize.init( osg::minimum( 4096u, maxSize ) );
-
-        OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
     }
 }
 
diff --git a/src/osgEarth/ColorFilter b/src/osgEarth/ColorFilter
index 29d160a..c47bdcd 100644
--- a/src/osgEarth/ColorFilter
+++ b/src/osgEarth/ColorFilter
@@ -31,6 +31,9 @@ namespace osgEarth
      * You can install a chain of ColorFilters on an ImageLayer and the shaders
      * will post-process the layer's color (after texturing but before lighting)
      * using custom shader code.
+     *
+     * NOTE: ColorFilter will probably be deprecated at some point down the line.
+     * Consider using a Layer shader instead.
      */
     class /*header-only*/ ColorFilter : public osg::Referenced
     {
@@ -130,7 +133,12 @@ namespace osgEarth
 
     // Macro used to register new annotation types.
 #define OSGEARTH_REGISTER_COLORFILTER( KEY, CLASSNAME ) \
+    extern "C" void osgearth_colorfilter_##KEY(void) {} \
     static osgEarth::ColorFilterRegistrationProxy< CLASSNAME > s_osgEarthColorFilterRegistrationProxy##KEY( #KEY )
+    
+#define USE_OSGEARTH_COLORFILTER( KEY ) \
+    extern "C" void osgearth_colorfilter_##KEY(void); \
+    static osgDB::PluginFunctionProxy proxy_osgearth_colorfilter_##KEY(osgearth_colorfilter_##KEY);
 
 } // namespace osgEarth
 
diff --git a/src/osgEarth/CompositeTileSource.cpp b/src/osgEarth/CompositeTileSource.cpp
index 8864753..f528802 100644
--- a/src/osgEarth/CompositeTileSource.cpp
+++ b/src/osgEarth/CompositeTileSource.cpp
@@ -56,7 +56,7 @@ CompositeTileSourceOptions::add( const ElevationLayerOptions& options )
 Config 
 CompositeTileSourceOptions::getConfig() const
 {    
-    Config conf = TileSourceOptions::newConfig();
+    Config conf = TileSourceOptions::getConfig();
 
     for( ComponentVector::const_iterator i = _components.begin(); i != _components.end(); ++i )
     {
@@ -155,7 +155,7 @@ CompositeTileSource::createImage(const TileKey&    key,
     {
         ImageLayer* layer = itr->get();
         ImageInfo imageInfo;
-        imageInfo.dataInExtents = layer->getTileSource()->hasDataInExtent( key.getExtent() );
+        imageInfo.dataInExtents = layer->mayHaveDataInExtent(key.getExtent());
         imageInfo.opacity = layer->getOpacity();
 
         if (imageInfo.dataInExtents)
@@ -165,6 +165,13 @@ CompositeTileSource::createImage(const TileKey&    key,
             {
                 imageInfo.image = image.getImage();
             }
+
+            // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
+            if (progress && (progress->isCanceled() || progress->needsRetry()))
+            {
+                OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
+                return 0L;
+            }
         }
 
         images.push_back(imageInfo);
@@ -205,6 +212,14 @@ CompositeTileSource::createImage(const TileKey&    key,
                     {
                         break;
                     }
+
+                    // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
+                    if (progress && (progress->isCanceled() || progress->needsRetry()))
+                    {
+                        OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
+                        return 0L;
+                    }
+
                     parentKey = parentKey.createParentKey();
                 }
 
@@ -279,8 +294,7 @@ osg::HeightField* CompositeTileSource::createHeightField(
             const TileKey&        key,
             ProgressCallback*     progress )
 {    
-    unsigned int size = *getOptions().tileSize();    
-    bool hae = false;
+    unsigned size = getPixelsPerTile(); //int size = *getOptions().tileSize();    
     osg::ref_ptr< osg::HeightField > heightField = new osg::HeightField();
     heightField->allocate(size, size);
 
@@ -291,7 +305,7 @@ osg::HeightField* CompositeTileSource::createHeightField(
     }  
 
     // Populate the heightfield and return it if it's valid
-    if (_elevationLayers.populateHeightField(heightField.get(), key, 0, INTERP_BILINEAR, progress))
+    if (_elevationLayers.populateHeightFieldAndNormalMap(heightField.get(), 0L, key, 0, INTERP_BILINEAR, progress))
     {                
         return heightField.release();
     }
@@ -319,7 +333,7 @@ CompositeTileSource::add( ImageLayer* layer )
     _imageLayers.push_back( layer );
     CompositeTileSourceOptions::Component comp;
     comp._layer = layer;
-    comp._imageLayerOptions = layer->getImageLayerOptions();
+    comp._imageLayerOptions = layer->options();
     _options._components.push_back( comp );    
 
     return true;
@@ -343,7 +357,7 @@ CompositeTileSource::add( ElevationLayer* layer )
     _elevationLayers.push_back( layer );
     CompositeTileSourceOptions::Component comp;
     comp._layer = layer;
-    comp._elevationLayerOptions = layer->getElevationLayerOptions();
+    comp._elevationLayerOptions = layer->options();
     _options._components.push_back( comp );    
 
     return true;
@@ -357,6 +371,8 @@ CompositeTileSource::initialize(const osgDB::Options* dbOptions)
 
     osg::ref_ptr<const Profile> profile = getProfile();
 
+    bool dataExtentsValid = true;
+
     for(CompositeTileSourceOptions::ComponentVector::iterator i = _options._components.begin();
         i != _options._components.end(); )
     {        
@@ -370,13 +386,13 @@ CompositeTileSource::initialize(const osgDB::Options* dbOptions)
             Status status = layer->open();
             if (status.isOK())
             {
-                i->_layer = layer;
-                _imageLayers.push_back( layer );
-                OE_INFO << LC << " .. added image layer " << layer->getName() << " (" << i->_imageLayerOptions->driver()->getDriver() << ")\n";
+                i->_layer = layer.get();
+                _imageLayers.push_back( layer.get() );
+                OE_INFO << LC << "Added image layer " << layer->getName() << " (" << i->_imageLayerOptions->driver()->getDriver() << ")\n";
             }
             else
             {
-                OE_WARN << LC << "Could not open image layer (" << layer->getName() << ") ... " << status.message() << std::endl;
+                OE_DEBUG << LC << "Could not open image layer (" << layer->getName() << ") ... " << status.message() << std::endl;
             }            
         }
         else if (i->_elevationLayerOptions.isSet() && !i->_layer.valid())
@@ -390,7 +406,8 @@ CompositeTileSource::initialize(const osgDB::Options* dbOptions)
             if (status.isOK())
             {
                 i->_layer = layer;
-                _elevationLayers.push_back( layer.get() );                
+                _elevationLayers.push_back( layer.get() );   
+                OE_INFO << LC << "Added elevation layer " << layer->getName() << " (" << i->_elevationLayerOptions->driver()->getDriver() << ")\n";             
             }
             else
             {
@@ -400,7 +417,7 @@ CompositeTileSource::initialize(const osgDB::Options* dbOptions)
 
         if ( !i->_layer.valid() )
         {
-            OE_WARN << LC << "A component has no valid TerrainLayer ... removing." << std::endl;
+            OE_DEBUG << LC << "A component has no valid TerrainLayer ... removing." << std::endl;            
             i = _options._components.erase( i );
         }
         else
@@ -416,23 +433,42 @@ CompositeTileSource::initialize(const osgDB::Options* dbOptions)
             _dynamic = _dynamic || source->isDynamic();
             
             // gather extents                        
-            const DataExtentList& extents = source->getDataExtents();            
-            for( DataExtentList::const_iterator j = extents.begin(); j != extents.end(); ++j )
-            {                
-                // Convert the data extent to the profile that is actually used by this TileSource
-                DataExtent dataExtent = *j;                
-                GeoExtent ext = dataExtent.transform(profile->getSRS());
-                unsigned int minLevel = 0;
-                unsigned int maxLevel = profile->getEquivalentLOD( source->getProfile(), *dataExtent.maxLevel() );                                        
-                dataExtent = DataExtent(ext, minLevel, maxLevel);                                
-                getDataExtents().push_back( dataExtent );
-            }          
+            const DataExtentList& extents = source->getDataExtents();  
+
+            // If even one of the layers' data extents is unknown, the entire composite
+            // must have unknown data extents:
+            if (extents.empty())
+            {
+                dataExtentsValid = false;
+                getDataExtents().clear();
+            }
+
+            if (dataExtentsValid)
+            {
+                for( DataExtentList::const_iterator j = extents.begin(); j != extents.end(); ++j )
+                {                
+                    // Convert the data extent to the profile that is actually used by this TileSource
+                    DataExtent dataExtent = *j;                
+                    GeoExtent ext = dataExtent.transform(profile->getSRS());
+                    unsigned int minLevel = 0;
+                    unsigned int maxLevel = profile->getEquivalentLOD( source->getProfile(), *dataExtent.maxLevel() );                                        
+                    dataExtent = DataExtent(ext, minLevel, maxLevel);                                
+                    getDataExtents().push_back( dataExtent );
+                }
+            }            
         }
 
         ++i;
     }
 
-    // set the new profile that was derived from the components
+    // If there is no profile set by the user or by a component, fall back
+    // on a default profile. This will allow the Layer to continue to operate
+    // off the cache even if all components fail to initialize for some reason.
+    if (profile.valid() == false)
+    {
+        profile = Profile::create("global-geodetic");
+    }
+
     setProfile( profile.get() );
 
     _initialized = true;
diff --git a/src/osgEarth/Config b/src/osgEarth/Config
index fda8ed3..0feeddc 100644
--- a/src/osgEarth/Config
+++ b/src/osgEarth/Config
@@ -155,8 +155,9 @@ namespace osgEarth
             }
         }
 
-        /** Copy of the first child with the given key */
-        Config child( const std::string& key ) const;
+        /** First child with the given key */
+        //Config child( const std::string& key ) const;
+        const Config& child( const std::string& key ) const;
 
         /** Pointer to the first child with the given key, or NULL if none exist */
         const Config* child_ptr( const std::string& key ) const;
@@ -245,22 +246,30 @@ namespace osgEarth
         /** Adds or replaces a value as a child, but only if it is set */
         template<typename T>
         void updateIfSet( const std::string& key, const optional<T>& opt ) {
+            remove(key);
             if ( opt.isSet() ) {
-                remove(key);
                 add( key, osgEarth::toString<T>( opt.value() ) );
             }
         }
+        template<typename T>
+        void set( const std::string& key, const optional<T>& opt ) {
+            updateIfSet(key, opt);
+        }
         
         /** Adds or replaces a referenced object as a child, but only if it is set */
         template<typename T>
         void updateObjIfSet( const std::string& key, const osg::ref_ptr<T>& opt ) {
+            remove(key);
             if ( opt.valid() ) {
-                remove(key);
                 Config conf = opt->getConfig();
                 conf.key() = key;
                 add( conf );
             }
         }
+        template<typename T>
+        void setObj( const std::string& key, const osg::ref_ptr<T>& opt ) {
+            updateObjIfSet(key, opt);
+        }
         
         /** Adds or replaces an object as a child, but only if it is set */
         template<typename T>
@@ -272,15 +281,25 @@ namespace osgEarth
                 add( conf );
             }
         }
+        template<typename T>
+        void setObj( const std::string& key, const optional<T>& obj ) {
+            updateObjIfSet(key, obj);
+        }
         
         /** Adds or replaces an enumeration value as a child, but only if it is set */
         template<typename X, typename Y>
         void updateIfSet( const std::string& key, const std::string& val, const optional<X>& target, const Y& targetValue ) {
-            if ( target.isSetTo( targetValue ) ) {
+            if (target.isSetTo(targetValue)) {
                 remove(key);
                 add( key, val );
             }
         }
+
+        // If target is set to targetValue, set key to val.
+        template<typename X, typename Y>
+        void set( const std::string& key, const std::string& val, const optional<X>& target, const Y& targetValue ) {
+            updateIfSet(key, val, target, targetValue);
+        }
         
         /** Adds or replaces value as a child */
         template<typename T>
@@ -293,6 +312,10 @@ namespace osgEarth
             remove(conf.key());
             add( conf );
         }
+        template<typename T>
+        void set( const std::string& key, const T& value ) {
+            update( key, value );
+        }
 
         /** Adds or replaces an object as a child */
         template<typename T>
@@ -302,11 +325,9 @@ namespace osgEarth
             conf.key() = key;
             add( conf );
         }
-
-        /** Same as update */
         template<typename T>
-        void set( const std::string& key, const T& value ) {
-            update( key, value );
+        void setObj( const std::string& key, const T& value ) {
+            updateObj(key, value);
         }
 
         /** Whether this object has the key OR has a child with the key */
@@ -466,6 +487,16 @@ namespace osgEarth
     }
 
     template<> inline
+    void Config::set<Config>( const std::string& key, const optional<Config>& opt ) {
+        remove(key);
+        if ( opt.isSet() ) {
+            Config conf = opt.value();
+            conf.key() = key;
+            add( conf );
+        }
+    }
+
+    template<> inline
     bool Config::getIfSet<Config>( const std::string& key, optional<Config>& output ) const {
         if ( hasChild( key ) ) {
             output = child(key);
@@ -529,7 +560,7 @@ namespace osgEarth
         ConfigOptions( const Config& conf =Config() )
             : _conf( conf ) { }
         ConfigOptions( const ConfigOptions& rhs )
-            : _conf( rhs.getConfig() ) { }
+            : _conf( rhs.getConfig() ) { } //rhs._conf ) { }
 
         virtual ~ConfigOptions();
         
@@ -548,11 +579,7 @@ namespace osgEarth
             mergeConfig( rhs.getConfig() );
         }
 
-        virtual Config getConfig() const { return _conf; }
-
-        virtual Config getConfig( bool isolate ) const { return isolate ? newConfig() : _conf; }
-
-        Config newConfig() const { Config c; c.setReferrer(referrer()); return c; }
+        virtual Config getConfig() const;
 
         bool empty() const { return _conf.empty(); }
 
@@ -584,11 +611,6 @@ namespace osgEarth
             conf.set("driver", _driver);
             return conf;
         }
-        virtual Config getConfig( bool isolate ) const {
-            Config conf = ConfigOptions::getConfig( isolate );
-            conf.set("driver", _driver);
-            return conf;
-        }
 
         virtual void mergeConfig( const Config& conf ) {
             ConfigOptions::mergeConfig(conf);
diff --git a/src/osgEarth/Config.cpp b/src/osgEarth/Config.cpp
index e29ec36..52b6b92 100644
--- a/src/osgEarth/Config.cpp
+++ b/src/osgEarth/Config.cpp
@@ -45,7 +45,8 @@ Config::setReferrer( const std::string& referrer )
         return;
 
     std::string absReferrer;
-    if( !osgDB::containsServerAddress( referrer ) ) {
+    if( !osgDB::containsServerAddress( referrer ) && !osgDB::isAbsolutePath( referrer ) ) {
+
         absReferrer = osgEarth::getAbsolutePath( referrer );
 
         if( osgEarth::isRelativePath( absReferrer ) )
@@ -80,6 +81,21 @@ Config::fromXML( std::istream& in )
     return xml.valid();
 }
 
+#if 1
+const Config&
+Config::child( const std::string& childName ) const
+{
+    for( ConfigSet::const_iterator i = _children.begin(); i != _children.end(); i++ ) {
+        if ( i->key() == childName )
+            return *i;
+    }
+    static Config s_emptyConf;
+    return s_emptyConf;
+    //Config emptyConf;
+    //emptyConf.setReferrer( _referrer );
+    //return emptyConf;
+}
+#else
 Config
 Config::child( const std::string& childName ) const
 {
@@ -87,11 +103,11 @@ Config::child( const std::string& childName ) const
         if ( i->key() == childName )
             return *i;
     }
-
     Config emptyConf;
     emptyConf.setReferrer( _referrer );
     return emptyConf;
 }
+#endif
 
 const Config*
 Config::child_ptr( const std::string& childName ) const
@@ -169,6 +185,16 @@ ConfigOptions::~ConfigOptions()
 {
 }
 
+Config
+ConfigOptions::getConfig() const
+{
+    // iniialize with the raw original conf. subclass getConfig's can 
+    // override the values there.
+    Config conf = _conf;
+    conf.setReferrer(referrer());
+    return conf;
+}
+
 /****************************************************************/
 DriverConfigOptions::~DriverConfigOptions()
 {
diff --git a/src/osgEarth/Containers b/src/osgEarth/Containers
index 881b03c..a340aec 100644
--- a/src/osgEarth/Containers
+++ b/src/osgEarth/Containers
@@ -314,14 +314,14 @@ namespace osgEarth
 
     public:
         LRUCache( unsigned max =100 ) : _max(max), _threadsafe(false) {
-            _buf = _max/10;
             _queries = 0;
             _hits = 0;
+            setMaxSize_impl(max);
         }
         LRUCache( bool threadsafe, unsigned max =100 ) : _max(max), _threadsafe(threadsafe) {
-            _buf = _max/10;
             _queries = 0;
             _hits = 0;
+            setMaxSize_impl(max);
         }
 
         /** dtor */
@@ -467,8 +467,8 @@ namespace osgEarth
         }
 
         void setMaxSize_impl( unsigned max ) {
-            _max = max;
-            _buf = max/10;
+            _max = std::max(max,10u);
+            _buf = _max/10u;
             while( _map.size() > _max ) {
                 const K& key = _lru.front();
                 _map.erase( key );
@@ -685,33 +685,42 @@ namespace osgEarth
     template<typename KEY, typename DATA>
     struct PerObjectFastMap
     {
+        struct Functor {
+            virtual void operator()(DATA& data) =0;
+        };
+
         DATA& get(KEY k)
         {
-            {
-                osgEarth::Threading::ScopedReadLock readLock(_mutex);
-                typename osgEarth::fast_map<KEY,DATA>::iterator i = _data.find(k);
-                if ( i != _data.end() )
-                    return i->second;
-            }
-            {
-                osgEarth::Threading::ScopedWriteLock lock(_mutex);
-                typename osgEarth::fast_map<KEY,DATA>::iterator i = _data.find(k);
-                if ( i != _data.end() )
-                    return i->second;
-                else
-                    return _data[k];
-            }
+            osgEarth::Threading::ScopedMutexLock lock(_mutex);
+            typename osgEarth::fast_map<KEY,DATA>::iterator i = _data.find(k);
+            if ( i != _data.end() )
+                return i->second;
+            else
+                return _data[k];
         }
 
         void remove(KEY k)
         {
-            osgEarth::Threading::ScopedWriteLock exclusive(_mutex);
+            osgEarth::Threading::ScopedMutexLock lock(_mutex);
             _data.erase( k );
         }
 
+        void forEach(Functor& functor)
+        {
+            osgEarth::Threading::ScopedMutexLock lock(_mutex);
+            for (typename fast_map<KEY, DATA>::iterator i = _data.begin(); i != _data.end(); ++i)
+                functor.operator()(i->second);
+        }
+
+        unsigned size() const
+        {
+            osgEarth::Threading::ScopedMutexLock lock(_mutex);
+            return _data.size();
+        }
+
     private:
-        osgEarth::fast_map<KEY,DATA>        _data;
-        osgEarth::Threading::ReadWriteMutex _mutex;
+        osgEarth::fast_map<KEY,DATA> _data;
+        mutable osgEarth::Threading::Mutex _mutex;
     };
 
     /** Template for thread safe per-object data storage */
@@ -890,6 +899,54 @@ namespace osgEarth
         std::set<osg::observer_ptr<T> >      _data;
         osgEarth::Threading::ReadWriteMutex  _mutex;
     };
+    
+
+    // borrowed from osg::buffered_object. Auto-resizing array.
+    template<class T>
+    class AutoArray
+    {
+    public:
+        inline AutoArray() { }
+
+        inline AutoArray(unsigned int size) : _array(size) { }
+
+        AutoArray& operator = (const AutoArray& rhs)
+        {
+            _array = rhs._array;
+            return *this;
+        }
+
+        inline void setAllElementsTo(const T& t) { std::fill(_array.begin(), _array.end(), t); }
+        inline void clear() { _array.clear(); }
+        inline bool empty() const { return _array.empty(); }
+        inline unsigned int size() const { return _array.size(); }
+        inline void resize(unsigned int newSize) { _array.resize(newSize); }
+
+        inline T& operator[] (unsigned int pos)
+        {
+            if (_array.size() <= pos)
+                _array.resize(pos + 1);
+
+            return _array[pos];
+        }
+
+        inline const T& operator[] (unsigned int pos) const
+        {
+            // automatically resize array.
+            if (_array.size() <= pos)
+                _array.resize(pos + 1);
+
+            return _array[pos];
+        }
+
+        inline T& back() { return _array[size()-1]; }
+        inline const T& back() const { return _array[size()-1]; }
+
+    protected:
+
+        mutable std::vector<T> _array;
+    };
+
 }
 
 #endif // OSGEARTH_CONTAINERS_H
diff --git a/src/osgEarth/Cube b/src/osgEarth/Cube
index 1345bcc..67b3ae1 100644
--- a/src/osgEarth/Cube
+++ b/src/osgEarth/Cube
@@ -144,7 +144,14 @@ namespace osgEarth
     protected: // SpatialReference overrides
 
         void _init();
-
+        
+        bool transformInFaceExtentToMBR(
+            const SpatialReference* to_srs,
+            int                     face,
+            double&                 in_out_xmin,
+            double&                 in_out_ymin,
+            double&                 in_out_xmax,
+            double&                 in_out_ymax ) const;
     };
 
     /**
diff --git a/src/osgEarth/Cube.cpp b/src/osgEarth/Cube.cpp
index c47970b..a6d55c5 100644
--- a/src/osgEarth/Cube.cpp
+++ b/src/osgEarth/Cube.cpp
@@ -527,6 +527,57 @@ CubeSpatialReference::transformExtentToMBR(const SpatialReference* to_srs,
                                            double&                 in_out_xmax,
                                            double&                 in_out_ymax ) const
 {
+    // input bounds:
+    Bounds inBounds(in_out_xmin, in_out_ymin, in_out_xmax, in_out_ymax);
+
+    Bounds outBounds;
+
+    // for each CUBE face, find the intersection of the input bounds and that face.
+    for (int face = 0; face < 6; ++face)
+    {
+        Bounds faceBounds( (double)(face), 0.0, (double)(face+1), 1.0);
+
+        Bounds intersection = faceBounds.intersectionWith(inBounds);
+
+        // if they intersect (with a non-zero area; abutting doesn't count in this case)
+        // transform the intersection and include in the result.
+        if (intersection.isValid() && intersection.area2d() > 0.0)
+        {
+            double
+                xmin = intersection.xMin(), ymin = intersection.yMin(),
+                xmax = intersection.xMax(), ymax = intersection.yMax();
+
+            if (transformInFaceExtentToMBR(to_srs, face, xmin, ymin, xmax, ymax))
+            {
+                outBounds.expandBy(Bounds(xmin, ymin, xmax, ymax));
+            }
+        }
+    }
+
+    if (outBounds.valid())
+    {
+        in_out_xmin = outBounds.xMin();
+        in_out_ymin = outBounds.yMin();
+        in_out_xmax = outBounds.xMax();
+        in_out_ymax = outBounds.yMax();
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool
+CubeSpatialReference::transformInFaceExtentToMBR(const SpatialReference* to_srs,
+                                                 int                     face,
+                                                 double&                 in_out_xmin,
+                                                 double&                 in_out_ymin,
+                                                 double&                 in_out_xmax,
+                                                 double&                 in_out_ymax ) const
+{
+    
+
     // note: this method only works when the extent is isolated to one face of the cube. If you
     // want to transform an artibrary extent, you need to break it up into separate extents for
     // each cube face.
@@ -535,7 +586,7 @@ CubeSpatialReference::transformExtentToMBR(const SpatialReference* to_srs,
     double face_xmin = in_out_xmin, face_ymin = in_out_ymin;
     double face_xmax = in_out_xmax, face_ymax = in_out_ymax;
 
-    int face;
+    //int face;
     CubeUtils::cubeToFace( face_xmin, face_ymin, face_xmax, face_ymax, face );
 
     // for equatorial faces, the normal transformation process will suffice (since it will call into
diff --git a/src/osgEarth/CullingUtils b/src/osgEarth/CullingUtils
index e6fd86f..c26ef49 100644
--- a/src/osgEarth/CullingUtils
+++ b/src/osgEarth/CullingUtils
@@ -89,6 +89,7 @@ namespace osgEarth
     
     /**
      * A simple culling-plane callback (a simpler version of ClusterCullingCallback)
+     * @deprecated
      */
     struct OSGEARTH_EXPORT CullNodeByNormal : public osg::NodeCallback {
         osg::Vec3d _normal;
@@ -96,6 +97,7 @@ namespace osgEarth
         void operator()(osg::Node* node, osg::NodeVisitor* nv);
     };
 
+    // @deprecated
     struct CullDrawableByNormal : public osg::Drawable::CullCallback {
         osg::Vec3d _normal;
         CullDrawableByNormal( const osg::Vec3d& normal ) : _normal(normal) { }
@@ -104,6 +106,7 @@ namespace osgEarth
         }
     };
 
+    // @deprecated
     struct OSGEARTH_EXPORT CullNodeByFrameNumber : public osg::NodeCallback {
         unsigned _frame;
         CullNodeByFrameNumber() : _frame(0) { }
@@ -113,10 +116,12 @@ namespace osgEarth
         }
     };
 
+    // @deprecated
     struct DisableSubgraphCulling : public osg::NodeCallback {
         void operator()(osg::Node* n, osg::NodeVisitor* v);
     };
 
+    // @deprecated
     struct StaticBound : public osg::Node::ComputeBoundingSphereCallback {
         osg::BoundingSphere _bs;
         StaticBound(const osg::BoundingSphere& bs) : _bs(bs) { }
@@ -125,6 +130,7 @@ namespace osgEarth
 
     // a cull callback that prevents objects from being included in the near/fear clip
     // plane calculates that OSG does.
+    // @deprecated
     struct OSGEARTH_EXPORT DoNotComputeNearFarCullCallback : public osg::NodeCallback
     {
         void operator()(osg::Node* node, osg::NodeVisitor* nv);
@@ -195,6 +201,9 @@ namespace osgEarth
         // access to the underlying cull visitor.
         osgUtil::CullVisitor* getCullVisitor() { return _cv; }
 
+        bool isCulledByProxyFrustum(osg::Node& node);
+        bool isCulledByProxyFrustum(const osg::BoundingBox& bbox);
+
     public: // proxy functions:
         osg::Vec3 getEyePoint() const;
         osg::Vec3 getViewPoint() const;
@@ -202,18 +211,17 @@ namespace osgEarth
         float getDistanceFromEyePoint(const osg::Vec3& pos, bool useLODScale) const;
         float getDistanceToViewPoint(const osg::Vec3& pos, bool useLODScale) const;
 
-    protected: // custom culling functions:
-
-        bool isCulledByProxyFrustum(osg::Node& node);
-        bool isCulledByProxyFrustum(const osg::BoundingBox& bbox);
+    protected:
         
         osgUtil::CullVisitor::value_type distance(const osg::Vec3& coord,const osg::Matrix& matrix);
 
         void handle_cull_callbacks_and_traverse(osg::Node& node);
 
         void apply(osg::Node& node);
-        void apply(osg::Transform& node);
-        void apply(osg::Geode& node);
+        void apply(osg::Transform& xform);
+        void apply(osg::Geode& geode);
+        void apply(osg::Drawable& drawable);
+        void apply(osg::LOD& lod);
     };
 
 
@@ -277,6 +285,30 @@ namespace osgEarth
     {
         Config dumpRenderBin(osgUtil::RenderBin* bin) const;
     };
+
+
+    /**
+     * Cull callback for cameras that sets the oe_ViewportSize uniform.
+     */
+    class OSGEARTH_EXPORT InstallViewportSizeUniform : public osg::NodeCallback
+    {
+    public:
+        void operator()(osg::Node* node, osg::NodeVisitor* nv) {
+            osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
+            const osg::Camera* camera = cv->getCurrentCamera();
+            osg::ref_ptr<osg::StateSet> ss;
+            if (camera && camera->getViewport()) {
+                ss = new osg::StateSet();
+                ss->addUniform(new osg::Uniform("oe_ViewportSize", osg::Vec2f(
+                    camera->getViewport()->width(),
+                    camera->getViewport()->height())));
+                cv->pushStateSet(ss.get());
+            }
+            traverse(node, nv);
+            if (ss.valid())
+                cv->popStateSet();
+        }
+    };
 }
 
 #endif // OSGEARTH_CULLING_UTILS_H
diff --git a/src/osgEarth/CullingUtils.cpp b/src/osgEarth/CullingUtils.cpp
index 1440a24..7115861 100644
--- a/src/osgEarth/CullingUtils.cpp
+++ b/src/osgEarth/CullingUtils.cpp
@@ -19,7 +19,7 @@
 #include <osgEarth/CullingUtils>
 #include <osgEarth/LineFunctor>
 #include <osgEarth/VirtualProgram>
-#include <osgEarth/DPLineSegmentIntersector>
+#include <osgUtil/LineSegmentIntersector>
 #include <osgEarth/GeoData>
 #include <osgEarth/Utils>
 #include <osg/ClusterCullingCallback>
@@ -678,7 +678,7 @@ void OcclusionCullingCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
                         osg::Vec3d vec = end-start; vec.normalize();
                         end -= vec*1.0;
 
-                        DPLineSegmentIntersector* i = new DPLineSegmentIntersector( start, end );
+                        osgUtil::LineSegmentIntersector* i = new osgUtil::LineSegmentIntersector( start, end );
                         i->setIntersectionLimit( osgUtil::Intersector::LIMIT_NEAREST );
                         osgUtil::IntersectionVisitor iv;
                         iv.setIntersector( i );
@@ -800,15 +800,9 @@ ProxyCullVisitor::distance(const osg::Vec3& coord,const osg::Matrix& matrix)
 void 
 ProxyCullVisitor::handle_cull_callbacks_and_traverse(osg::Node& node)
 {
-#if OSG_VERSION_GREATER_THAN(3,3,1)
     osg::Callback* callback = node.getCullCallback();
     if (callback) callback->run(&node, this);
     else traverse(node);
-#else
-    osg::NodeCallback* callback = node.getCullCallback();
-    if (callback) (*callback)(&node,this);
-    else traverse(node);
-#endif
 }
 
 void 
@@ -874,53 +868,58 @@ ProxyCullVisitor::apply(osg::Transform& node)
 void 
 ProxyCullVisitor::apply(osg::Geode& node)
 {
-    //OE_INFO << "Geode!" << std::endl;
+    ProxyCullVisitor::apply(static_cast<osg::Node&>(node));
+}
 
-    if ( isCulledByProxyFrustum(node) )
+void 
+ProxyCullVisitor::apply(osg::LOD& node)
+{
+    ProxyCullVisitor::apply(static_cast<osg::Node&>(node));
+}
+
+void
+ProxyCullVisitor::apply(osg::Drawable& drawable)
+{
+    if ( isCulledByProxyFrustum(drawable) )
         return;
 
-    _cv->pushOntoNodePath( &node );
+    _cv->pushOntoNodePath( &drawable );
 
     // push the node's state.
-    osg::StateSet* node_state = node.getStateSet();
+    osg::StateSet* node_state = drawable.getStateSet();
     if (node_state) _cv->pushStateSet(node_state);
 
-    // traverse any call callbacks and traverse any children.
-    handle_cull_callbacks_and_traverse(node);
-
     osg::RefMatrix& matrix = *_cv->getModelViewMatrix();
-    for(unsigned int i=0;i<node.getNumDrawables();++i)
-    {
-        osg::Drawable* drawable = node.getDrawable(i);
-        const osg::BoundingBox& bb = Utils::getBoundingBox(drawable);
+    const osg::BoundingBox& bb = Utils::getBoundingBox(&drawable);
 
-        if( drawable->getCullCallback() )
-        {
-#if OSG_VERSION_GREATER_THAN(3,3,1)
-            if( drawable->getCullCallback()->run(drawable, _cv) == true )
-                continue;
-#else
-            if( drawable->getCullCallback()->cull( _cv, drawable, &_cv->getRenderInfo() ) == true )
-                continue;
-#endif
-        }
+    bool culledOut = false;
 
-        //else
-        {
-            if (node.isCullingActive() && isCulledByProxyFrustum(bb)) continue;
-        }
+    if( drawable.getCullCallback() )
+    {
+        if (drawable.getCullCallback()->run(&drawable, _cv) == true)
+            culledOut = true;
+    }
+
+    if (!culledOut)
+    {
+        if (drawable.isCullingActive() && isCulledByProxyFrustum(bb)) 
+            culledOut = true;
+    }
 
 
-        if ( _cv->getComputeNearFarMode() && bb.valid())
-        {
-            if (!_cv->updateCalculatedNearFar(matrix,*drawable,false)) continue;
-        }
+    if ( !culledOut && _cv->getComputeNearFarMode() && bb.valid())
+    {
+        if (!_cv->updateCalculatedNearFar(matrix,drawable,false))
+            culledOut = true;
+    }
 
+    if (!culledOut)
+    {
         // need to track how push/pops there are, so we can unravel the stack correctly.
         unsigned int numPopStateSetRequired = 0;
 
         // push the geoset's state on the geostate stack.
-        osg::StateSet* stateset = drawable->getStateSet();
+        osg::StateSet* stateset = drawable.getStateSet();
         if (stateset)
         {
             ++numPopStateSetRequired;
@@ -954,14 +953,13 @@ ProxyCullVisitor::apply(osg::Geode& node)
         }
         else
         {
-            _cv->addDrawableAndDepth(drawable,&matrix,depth);
+            _cv->addDrawableAndDepth(&drawable,&matrix,depth);
         }
 
         for(unsigned int i=0;i< numPopStateSetRequired; ++i)
         {
             _cv->popStateSet();
         }
-
     }
 
     // pop the node's state off the geostate stack.
@@ -978,7 +976,7 @@ namespace
         "#version " GLSL_VERSION_STR "\n"
         GLSL_DEFAULT_PRECISION_FLOAT "\n"
         "uniform mat4 osg_ViewMatrix; \n"
-        "varying float oe_horizon_alpha; \n"
+        "out float oe_horizon_alpha; \n"
         "void oe_horizon_vertex(inout vec4 VertexVIEW) \n"
         "{ \n"
         "    const float scale     = 0.001; \n"                 // scale factor keeps dots&crosses in SP range
@@ -1002,7 +1000,7 @@ namespace
     const char* horizon_fs =
         "#version " GLSL_VERSION_STR "\n"
         GLSL_DEFAULT_PRECISION_FLOAT "\n"
-        "varying float oe_horizon_alpha; \n"
+        "in float oe_horizon_alpha; \n"
         "void oe_horizon_fragment(inout vec4 color) \n"
         "{ \n"
         "    color.a *= oe_horizon_alpha; \n"
diff --git a/src/osgEarth/DPLineSegmentIntersector b/src/osgEarth/DPLineSegmentIntersector
index 2ce1a5d..ace9bb3 100644
--- a/src/osgEarth/DPLineSegmentIntersector
+++ b/src/osgEarth/DPLineSegmentIntersector
@@ -28,17 +28,17 @@ namespace osgEarth
      * A double-precision version of the osgUtil::LineSegmentIntersector.
      * Use this instead of the OSG one when working in geocentric space.
      */
-    class OSGEARTH_EXPORT DPLineSegmentIntersector : public osgUtil::LineSegmentIntersector
+    class OSGEARTH_EXPORT osgUtil::LineSegmentIntersector : public osgUtil::LineSegmentIntersector
     {
     public:
-        DPLineSegmentIntersector(const osgUtil::Intersector::CoordinateFrame& cf, const osg::Vec3d& start, const osg::Vec3d& end)
+        osgUtil::LineSegmentIntersector(const osgUtil::Intersector::CoordinateFrame& cf, const osg::Vec3d& start, const osg::Vec3d& end)
             : osgUtil::LineSegmentIntersector(cf, start, end) { }
 
-        DPLineSegmentIntersector(const osg::Vec3d& start, const osg::Vec3d& end)
+        osgUtil::LineSegmentIntersector(const osg::Vec3d& start, const osg::Vec3d& end)
             : osgUtil::LineSegmentIntersector(start, end) { }
 
         /** dtor */
-        virtual ~DPLineSegmentIntersector() { }
+        virtual ~osgUtil::LineSegmentIntersector() { }
 
     public: // overrides
         
diff --git a/src/osgEarth/DPLineSegmentIntersector.cpp b/src/osgEarth/DPLineSegmentIntersector.cpp
index 1d151a7..462bca4 100644
--- a/src/osgEarth/DPLineSegmentIntersector.cpp
+++ b/src/osgEarth/DPLineSegmentIntersector.cpp
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU Lesser General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
-#include <osgEarth/DPLineSegmentIntersector>
+#include <osgEarth/osgUtil::LineSegmentIntersector>
 #include <osgEarth/Utils>
 #include <osg/TriangleFunctor>
 #include <osg/KdTree>
@@ -209,12 +209,12 @@ namespace
 //----------------------------------------------------------------------------    
 
 osgUtil::Intersector* 
-DPLineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
+osgUtil::LineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
 {
     if (_coordinateFrame==MODEL && iv.getModelMatrix()==0)
     {
         //GW: changed the next line
-        osg::ref_ptr<DPLineSegmentIntersector> lsi = new DPLineSegmentIntersector(_start, _end);
+        osg::ref_ptr<osgUtil::LineSegmentIntersector> lsi = new osgUtil::LineSegmentIntersector(_start, _end);
         lsi->_parent = this;
         lsi->_intersectionLimit = this->_intersectionLimit;
         return lsi.release();
@@ -249,7 +249,7 @@ DPLineSegmentIntersector::clone(osgUtil::IntersectionVisitor& iv)
     inverse.invert(matrix);
 
     //GW: changed the next line
-    osg::ref_ptr<DPLineSegmentIntersector> lsi = new DPLineSegmentIntersector(_start * inverse, _end * inverse);
+    osg::ref_ptr<osgUtil::LineSegmentIntersector> lsi = new osgUtil::LineSegmentIntersector(_start * inverse, _end * inverse);
     lsi->_parent = this;
     lsi->_intersectionLimit = this->_intersectionLimit;
     return lsi.release();
@@ -259,7 +259,7 @@ void intersectWithKdTree(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawab
     , const osg::Vec3d s, const osg::Vec3d e
     , const osg::Vec3d _start, const osg::Vec3d _end
     , osg::KdTree* kdTree
-    , DPLineSegmentIntersector& intersector)
+    , osgUtil::LineSegmentIntersector& intersector)
 {
     osg::KdTree::LineSegmentIntersections intersections;
     intersections.reserve(4);
@@ -321,7 +321,7 @@ void intersectWithQuadTree(osgUtil::IntersectionVisitor& iv, osg::Drawable* draw
     , const osg::Vec3d s, const osg::Vec3d e
     , const osg::Vec3d _start, const osg::Vec3d _end
     , QuadTree* quadTree
-    , DPLineSegmentIntersector& intersector)
+    , osgUtil::LineSegmentIntersector& intersector)
 {
     QuadTree::LineSegmentIntersections intersections;
     intersections.reserve(4);
@@ -379,7 +379,7 @@ void intersectWithQuadTree(osgUtil::IntersectionVisitor& iv, osg::Drawable* draw
     }
 }
 void
-DPLineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)
+osgUtil::LineSegmentIntersector::intersect(osgUtil::IntersectionVisitor& iv, osg::Drawable* drawable)
 {
     if (reachedLimit()) return;
 
diff --git a/src/osgEarth/DateTime b/src/osgEarth/DateTime
index 1f1b5a3..1d38bbb 100644
--- a/src/osgEarth/DateTime
+++ b/src/osgEarth/DateTime
@@ -53,7 +53,7 @@ namespace osgEarth
         /** DateTime from UTC seconds since the epoch */
         DateTime(TimeStamp utc);
 
-        /** DateTime from year, month, date, hours */
+        /** DateTime from year, month [1-12], date [1-31], hours [0-24) */
         DateTime(int year, int month, int day, double hours);
 
         /** DateTime from an ISO 8601 string */
diff --git a/src/osgEarth/DateTimeRange b/src/osgEarth/DateTimeRange
index a1c7bd0..61adc20 100644
--- a/src/osgEarth/DateTimeRange
+++ b/src/osgEarth/DateTimeRange
@@ -27,19 +27,32 @@
 
 namespace osgEarth
 {
+    /**
+     * Two DateTime objects representing a temporal range.
+     */
     class OSGEARTH_EXPORT DateTimeRange
     {
     public:
+        //! Start of date/time range
         optional<DateTime>& begin() { return _begin; }
+        //! Start of date/time range
         const optional<DateTime>& begin() const { return _begin; }
 
+        //! End of date/time range
         optional<DateTime>& end() { return _end; }
+        //! End of date/time range
         const optional<DateTime>& end() const { return _end; }
 
+        //! Expand the range to include a date/time
         void expandBy(const DateTime& other);
+
+        //! Expand the range to include another date/time range
         void expandBy(const DateTimeRange& other);
 
+        //! True is the given date/time falls within this range
         bool intersects(const DateTime& other) const;
+
+        //! True is the given range falls within this range
         bool intersects(const DateTimeRange& other) const;
 
     private:
diff --git a/src/osgEarth/DepthOffset b/src/osgEarth/DepthOffset
index 4b4ed6a..cab2e63 100644
--- a/src/osgEarth/DepthOffset
+++ b/src/osgEarth/DepthOffset
@@ -22,6 +22,7 @@
 
 #include <osgEarth/Common>
 #include <osgEarth/Config>
+#include <osgEarth/Units>
 #include <osg/Group>
 #include <osg/Program>
 #include <osg/Uniform>
@@ -51,8 +52,8 @@
 namespace osgEarth
 {
     /**
-    * Depth Offsetting options.
-    */
+     * Depth Offsetting options.
+     */
     class OSGEARTH_EXPORT DepthOffsetOptions
     {
     public:
@@ -64,20 +65,20 @@ namespace osgEarth
         const optional<bool>& enabled() const { return _enabled; }
 
         /** depth bias (in meters) applied at the minimum camera range. */
-        optional<float>& minBias() { return _minBias; }
-        const optional<float>& minBias() const { return _minBias; }
+        optional<Distance>& minBias() { return _minBias; }
+        const optional<Distance>& minBias() const { return _minBias; }
 
         /** depth bias (in meters) applied at the maximum camera range. */
-        optional<float>& maxBias() { return _maxBias; }
-        const optional<float>& maxBias() const { return _maxBias; }
+        optional<Distance>& maxBias() { return _maxBias; }
+        const optional<Distance>& maxBias() const { return _maxBias; }
 
         /** camera range (in meters) at which to apply the minimum depth bias. */
-        optional<float>& minRange() { return _minRange; }
-        const optional<float>& minRange() const { return _minRange; }
+        optional<Distance>& minRange() { return _minRange; }
+        const optional<Distance>& minRange() const { return _minRange; }
 
         /** camera range (in meters) at which to apply the maximum depth bias. */
-        optional<float>& maxRange() { return _maxRange; }
-        const optional<float>& maxRange() const { return _maxRange; }
+        optional<Distance>& maxRange() { return _maxRange; }
+        const optional<Distance>& maxRange() const { return _maxRange; }
 
         /** automatic calculation of the minRange based on geometry analysis */
         optional<bool>& automatic() { return _auto; }
@@ -88,10 +89,10 @@ namespace osgEarth
 
     private:
         optional<bool>  _enabled;
-        optional<float> _minBias;
-        optional<float> _maxBias;
-        optional<float> _minRange;
-        optional<float> _maxRange;
+        optional<Distance> _minBias;
+        optional<Distance> _maxBias;
+        optional<Distance> _minRange;
+        optional<Distance> _maxRange;
         optional<bool>  _auto;
     };
 
@@ -142,8 +143,6 @@ namespace osgEarth
         bool                         _supported;
         bool                         _dirty;
         osg::observer_ptr<osg::Node> _graph;
-        //osg::ref_ptr<osg::Uniform>   _biasUniform;
-        //osg::ref_ptr<osg::Uniform>   _rangeUniform;
         osg::ref_ptr<osg::Uniform>   _minBiasUniform, _maxBiasUniform;
         osg::ref_ptr<osg::Uniform>   _minRangeUniform, _maxRangeUniform;
         DepthOffsetOptions           _options;
diff --git a/src/osgEarth/DepthOffset.cpp b/src/osgEarth/DepthOffset.cpp
index 0ff7b01..c6bdae5 100644
--- a/src/osgEarth/DepthOffset.cpp
+++ b/src/osgEarth/DepthOffset.cpp
@@ -28,6 +28,7 @@
 
 #include <osg/Geode>
 #include <osg/Geometry>
+#include <osg/Depth>
 
 #define LC "[DepthOffset] "
 
@@ -167,8 +168,7 @@ DepthOffsetAdapter::setGraph(osg::Node* graph)
         (graphChanging || (_options.enabled() == false));
 
     bool install =
-        (graph && graphChanging ) || 
-        (graph && (_options.enabled() == true));
+        (graph && graphChanging && _options.enabled() == true);
 
     // shader package:
     Shaders shaders;
@@ -185,6 +185,8 @@ DepthOffsetAdapter::setGraph(osg::Node* graph)
         s->removeUniform( _maxRangeUniform.get() );
         
         shaders.unload( VirtualProgram::get(s), shaders.DepthOffsetVertex );
+
+        s->removeAttribute(osg::StateAttribute::DEPTH);
     }
 
     if ( install )
@@ -193,12 +195,19 @@ DepthOffsetAdapter::setGraph(osg::Node* graph)
 
         // install uniforms and shaders.
         osg::StateSet* s = graph->getOrCreateStateSet();
+
+        // so the stateset doesn't get merged by a state set optimizer
+        s->setDataVariance(s->DYNAMIC);
+
         s->addUniform( _minBiasUniform.get() );
         s->addUniform( _maxBiasUniform.get() );
         s->addUniform( _minRangeUniform.get() );
         s->addUniform( _maxRangeUniform.get() );
         
-        shaders.load(VirtualProgram::getOrCreate(s), shaders.DepthOffsetVertex);        
+        shaders.load(VirtualProgram::getOrCreate(s), shaders.DepthOffsetVertex);    
+
+        // disable depth writes
+        s->setAttributeAndModes(new osg::Depth(osg::Depth::LEQUAL, 0.0, 1.0, false), 1);
     }
 
     if ( graphChanging )
@@ -216,17 +225,10 @@ DepthOffsetAdapter::updateUniforms()
 {
     if ( !_supported ) return;
 
-    _minBiasUniform->set( *_options.minBias() );
-    _maxBiasUniform->set( *_options.maxBias() );
-    _minRangeUniform->set( *_options.minRange() );
-    _maxRangeUniform->set( *_options.maxRange() );
-
-    if ( _options.enabled() == true )
-    {
-        OE_TEST << LC 
-            << "bias=[" << *_options.minBias() << ", " << *_options.maxBias() << "] ... "
-            << "range=[" << *_options.minRange() << ", " << *_options.maxRange() << "]" << std::endl;
-    }
+    _minBiasUniform->set( (float)_options.minBias()->as(Units::METERS) );
+    _maxBiasUniform->set( (float)_options.maxBias()->as(Units::METERS) );
+    _minRangeUniform->set( (float)_options.minRange()->as(Units::METERS) );
+    _maxRangeUniform->set( (float)_options.maxRange()->as(Units::METERS) );
 }
 
 void 
diff --git a/src/osgEarth/DepthOffset.vert.glsl b/src/osgEarth/DepthOffset.vert.glsl
index 2ddee8c..2f2d760 100644
--- a/src/osgEarth/DepthOffset.vert.glsl
+++ b/src/osgEarth/DepthOffset.vert.glsl
@@ -10,6 +10,10 @@ uniform float oe_depthOffset_maxBias;
 uniform float oe_depthOffset_minRange;
 uniform float oe_depthOffset_maxRange;
 
+//uniform mat4 gl_ProjectionMatrix;
+
+float oe_depthOffset_biasClip;
+
 void oe_depthOffset_vertex(inout vec4 vertexView)
 {
     // calculate range to target:
@@ -22,9 +26,18 @@ void oe_depthOffset_vertex(inout vec4 vertexView)
 	// clamp the bias to 1/2 of the range of the vertex. We don't want to 
     // pull the vertex TOO close to the camera and certainly not behind it.
     bias = min(bias, range*0.5);
+    bias = min(bias, oe_depthOffset_maxBias);
+
+    //vec4 p0 = gl_ProjectionMatrix * vec4(0,0,0,1);
+    //vec4 p1 = gl_ProjectionMatrix * vec4(0,0,-bias,1);
+    //oe_depthOffset_biasClip = distance(p0, p1);
+    //oe_depthOffset_biasClip = p1.z;
+
+    //vec4 refPointClip = gl_ProjectionMatrix * vec4(0.0, 0.0, -bias, 1.0);
+    //oe_depthOffset_biasClip = refPointClip.z;
 
     //   pull the vertex towards the camera.
     vec3 pullVec = normalize(vertexView.xyz);
     vec3 simVert3 = vertexView.xyz - pullVec*bias;
     vertexView = vec4(simVert3, 1.0);
-}
\ No newline at end of file
+}
diff --git a/src/osgEarth/DrapeableNode b/src/osgEarth/DrapeableNode
index c0f2bbc..4b89bdf 100644
--- a/src/osgEarth/DrapeableNode
+++ b/src/osgEarth/DrapeableNode
@@ -22,20 +22,19 @@
 
 #include <osgEarth/Common>
 #include <osgEarth/optional>
+#include <osgEarth/MapNode>
+#include <osgEarth/MapNodeObserver>
 #include <osg/Group>
 
 namespace osgEarth
 {
-    class MapNode;
-
     /**
-     * Base class for a graph that can be "draped" on the MapNode terrain
-     * using the overlay decorator.
+     * Base class for a graph that can be "draped" on the terrain using
+     * projective texturing.
      *
-     * Usage: Create this node and put it anywhere in the scene graph. The
-     * subgraph of this node will be draped on the MapNode's terrain.
+     * Usage: Create this node and place it as a descendant of a MapNode.
      */
-    class OSGEARTH_EXPORT DrapeableNode : public osg::Group
+    class OSGEARTH_EXPORT DrapeableNode : public osg::Group, public MapNodeObserver
     {
     public:
         META_Object(osgEarth, DrapeableNode);
@@ -58,12 +57,18 @@ namespace osgEarth
 
         virtual void traverse(osg::NodeVisitor& nv);
 
+    public: // osgEarth::MapNodeObserver
+
+        virtual void setMapNode(MapNode* mapNode) { _mapNode = mapNode; }
+        virtual MapNode* getMapNode() { return _mapNode.get(); }
 
     protected:
         /** dtor */
         virtual ~DrapeableNode() { }
 
         bool _drapingEnabled;
+        bool _updateRequested;
+        osg::observer_ptr<MapNode> _mapNode;
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/DrapeableNode.cpp b/src/osgEarth/DrapeableNode.cpp
index 1f6b488..26ef03c 100644
--- a/src/osgEarth/DrapeableNode.cpp
+++ b/src/osgEarth/DrapeableNode.cpp
@@ -21,6 +21,9 @@
 #include <osgEarth/DrapingCullSet>
 #include <osgEarth/Registry>
 #include <osgEarth/CullingUtils>
+#include <osgEarth/MapNode>
+#include <osgEarth/NodeUtils>
+#include <osgEarth/TerrainEngineNode>
 
 #define LC "[DrapeableNode] "
 
@@ -28,7 +31,8 @@ using namespace osgEarth;
 
 
 DrapeableNode::DrapeableNode() :
-_drapingEnabled( true )
+_drapingEnabled( true ),
+_updateRequested( true )
 {
     // Unfortunetly, there's no way to return a correct bounding sphere for
     // the node since the draping will move it to the ground. The bounds
@@ -36,6 +40,9 @@ _drapingEnabled( true )
     // have to ensure that this node makes it into the draping cull set so it
     // can be frustum-culled at the proper time.
     setCullingActive( !_drapingEnabled );
+
+    // activate an update traversal to find the MapNode
+    ADJUST_UPDATE_TRAV_COUNT(this, +1);
 }
 
 DrapeableNode::DrapeableNode(const DrapeableNode& rhs, const osg::CopyOp& copy) :
@@ -59,16 +66,37 @@ DrapeableNode::traverse(osg::NodeVisitor& nv)
 {
     if ( _drapingEnabled && nv.getVisitorType() == nv.CULL_VISITOR )
     {
-        // access the cull visitor:
-        osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
-
         // find the cull set for this camera:
-        DrapingCullSet& cullSet = DrapingCullSet::get( cv->getCurrentCamera() );
-        cullSet.push( this, cv->getNodePath(), nv.getFrameStamp() );
+        osg::ref_ptr<MapNode> mapNode;
+        if (_mapNode.lock(mapNode))
+        {
+            osgUtil::CullVisitor* cv = Culling::asCullVisitor(nv);
+            DrapingCullSet& cullSet = mapNode->getDrapingManager()->get( cv->getCurrentCamera() );
+            cullSet.push( this, cv->getNodePath(), nv.getFrameStamp() );
+        }
+    }
+
+    else if (_drapingEnabled && nv.getVisitorType() == nv.UPDATE_VISITOR)
+    {        
+        if (_updateRequested)
+        {
+            if (_mapNode.valid() == false)
+            {
+                _mapNode = osgEarth::findInNodePath<MapNode>(nv);
+            }
+
+            if (_mapNode.valid())
+            {
+                _updateRequested = false;
+                ADJUST_UPDATE_TRAV_COUNT(this, -1);
+            }
+        }
+
+        osg::Group::traverse(nv);
     }
     else
     {
-        osg::Group::traverse( nv );
+        osg::Group::traverse(nv);
     }
 }
 
diff --git a/src/osgEarth/Draping.frag.glsl b/src/osgEarth/Draping.frag.glsl
index 45da38b..e07ea49 100644
--- a/src/osgEarth/Draping.frag.glsl
+++ b/src/osgEarth/Draping.frag.glsl
@@ -5,15 +5,18 @@ $GLSL_DEFAULT_PRECISION_FLOAT
 #pragma vp_location   fragment_coloring
 #pragma vp_order      0.6
 
-uniform bool oe_isPickCamera;
+#pragma import_defines(OE_IS_PICK_CAMERA)
+
 uniform sampler2D oe_overlay_tex;
 in vec4 oe_overlay_texcoord;
 
 void oe_overlay_fragment(inout vec4 color)
 {
-    vec4 texel = texture2DProj(oe_overlay_tex, oe_overlay_texcoord);
-    vec4 blendedTexel = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a);
+    vec4 texel = textureProj(oe_overlay_tex, oe_overlay_texcoord);
 
-    float pick = oe_isPickCamera? 1.0 : 0.0;
-    color = mix(blendedTexel, texel, pick);
+#ifdef OE_IS_PICK_CAMERA
+    color = texel;
+#else
+    color = vec4( mix( color.rgb, texel.rgb, texel.a ), color.a);
+#endif
 }
diff --git a/src/osgEarth/Draping.vert.glsl b/src/osgEarth/Draping.vert.glsl
index ce8c4b1..d123bc2 100644
--- a/src/osgEarth/Draping.vert.glsl
+++ b/src/osgEarth/Draping.vert.glsl
@@ -12,4 +12,4 @@ out vec4 oe_overlay_texcoord;
 void oe_overlay_vertex(inout vec4 vertexVIEW)
 {
     oe_overlay_texcoord = oe_overlay_texmatrix * vertexVIEW;
-}
\ No newline at end of file
+}
diff --git a/src/osgEarth/DrapingCullSet b/src/osgEarth/DrapingCullSet
index c9db48a..4b4e23c 100644
--- a/src/osgEarth/DrapingCullSet
+++ b/src/osgEarth/DrapingCullSet
@@ -25,6 +25,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/Containers>
 #include <osgEarth/DrapeableNode>
+#include <osgEarth/ThreadingUtils>
 #include <osg/Camera>
 #include <osg/ObserverNodePath>
 
@@ -33,14 +34,11 @@ namespace osgEarth
     /**
      * Culling set for tracking groups whose contents should be "draped",
      * i.e. rendered to a texture and projected onto the terrain.
+     * Internal object - do not use directly. Use a DrapeableNode instead.
      */
-    class OSGEARTH_EXPORT DrapingCullSet
+    class OSGEARTH_INTERNAL DrapingCullSet
     {
     public:
-        /** Gets the per-thread cull set associated with a camera. */
-        static DrapingCullSet& get(const osg::Camera*);
-
-    public:
         struct Entry
         {            
             osg::ref_ptr<osg::Group>     _node;
@@ -71,6 +69,20 @@ namespace osgEarth
         bool                _frameCulled;
     };
 
+    /**
+     * Houses all the active DrapingCullSets under an osgEarth TerrainEngine.
+     * Internal node - so not use directly.
+     */
+    class OSGEARTH_INTERNAL DrapingManager
+    {
+    public:
+        //! Gets the draping cull set associated with a camera.
+        DrapingCullSet& get(const osg::Camera*);
+
+    private:
+        PerObjectFastMap<const osg::Camera*, DrapingCullSet> _sets;
+    };
+
 } // namespace osgEarth
 
 #endif // OSGEARTH_DRAPING_CULL_SET
diff --git a/src/osgEarth/DrapingCullSet.cpp b/src/osgEarth/DrapingCullSet.cpp
index 0d07dae..96d375b 100644
--- a/src/osgEarth/DrapingCullSet.cpp
+++ b/src/osgEarth/DrapingCullSet.cpp
@@ -27,20 +27,19 @@
 using namespace osgEarth;
 
 
-
-
 DrapingCullSet&
-DrapingCullSet::get(const osg::Camera* cam)
-{    
-    static PerObjectFastMap<const osg::Camera*, DrapingCullSet> sets;
-
+DrapingManager::get(const osg::Camera* cam)
+{
     // Known issue: it is possible for a draping cull set to be "orphaned" - this
     // would happen if the cull set were populated and then not used. This is a
     // very unlikely scenario (because the scene graph would have to change mid-cull)
     // but nevertheless possible.
-    return sets.get(cam);
+    return _sets.get(cam);
 }
 
+//............................................................................
+
+
 DrapingCullSet::DrapingCullSet() :
 _frameCulled( true )
 {
@@ -64,7 +63,9 @@ DrapingCullSet::push(DrapeableNode* node, const osg::NodePath& path, const osg::
     entry._path.setNodePath( path );
     entry._matrix = new osg::RefMatrix( osg::computeLocalToWorld(path) );
     entry._frame = fs ? fs->getFrameNumber() : 0;
-    _bs.expandBy( node->getBound() );
+    _bs.expandBy( osg::BoundingSphere(
+        node->getBound().center() * (*entry._matrix.get()),
+        node->getBound().radius() ));
 }
 
 void
@@ -144,12 +145,4 @@ DrapingCullSet::accept(osg::NodeVisitor& nv)
         // mark this set so it will reset for the next frame
         _frameCulled = true;
     }
-
-    else
-    {
-        for( std::vector<Entry>::iterator entry = _entries.begin(); entry != _entries.end(); ++entry )
-        {
-            
-        }
-    }
 }
diff --git a/src/osgEarth/DrapingTechnique b/src/osgEarth/DrapingTechnique
index 827894e..7ad477f 100644
--- a/src/osgEarth/DrapingTechnique
+++ b/src/osgEarth/DrapingTechnique
@@ -25,6 +25,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/OverlayDecorator>
 #include <osgEarth/DrapeableNode>
+#include <osgEarth/DrapingCullSet>
 #include <osg/TexGenNode>
 #include <osg/Uniform>
 #include <vector>
@@ -42,7 +43,7 @@ namespace osgEarth
      * This class is similar in scope to osgSim::OverlayNode, but is optimized
      * for use with osgEarth and geocentric terrains.
      */
-    class OSGEARTH_EXPORT DrapingTechnique : public OverlayTechnique
+    class OSGEARTH_INTERNAL DrapingTechnique : public OverlayTechnique
     {
     public:
         DrapingTechnique();
@@ -100,6 +101,8 @@ namespace osgEarth
         virtual bool hasData(
             OverlayDecorator::TechRTTParams& params) const;
 
+        virtual bool optimizeToVisibleBound() const { return true; }
+
         void preCullTerrain(
             OverlayDecorator::TechRTTParams& params,
             osgUtil::CullVisitor*            cv );
@@ -127,6 +130,10 @@ namespace osgEarth
         bool                          _attachStencil;
         double                        _maxFarNearRatio;
 
+        mutable DrapingManager _drapingManager;
+        DrapingManager& getDrapingManager() { return _drapingManager; }
+        friend class MapNode;
+
         struct TechData : public osg::Referenced
         {
             osg::ref_ptr<osg::Uniform> _texGenUniform;
diff --git a/src/osgEarth/DrapingTechnique.cpp b/src/osgEarth/DrapingTechnique.cpp
index e211e4b..a83c288 100644
--- a/src/osgEarth/DrapingTechnique.cpp
+++ b/src/osgEarth/DrapingTechnique.cpp
@@ -27,6 +27,7 @@
 #include <osgEarth/VirtualProgram>
 #include <osgEarth/Shaders>
 #include <osgEarth/CullingUtils>
+#include <osgEarth/Lighting>
 
 #include <osg/BlendFunc>
 #include <osg/TexGen>
@@ -54,7 +55,7 @@ namespace
     class DrapingCamera : public osg::Camera
     {
     public:
-        DrapingCamera() : osg::Camera(), _camera(0L)
+        DrapingCamera(DrapingManager& dm) : osg::Camera(), _dm(dm), _camera(0L)
         {
             setCullingActive( false );
         }
@@ -68,13 +69,14 @@ namespace
         }
 
         void traverse(osg::NodeVisitor& nv)
-        {            
-            DrapingCullSet& cullSet = DrapingCullSet::get(_camera);
+        {
+            DrapingCullSet& cullSet = _dm.get(_camera);
             cullSet.accept( nv );
         }
 
     protected:
         virtual ~DrapingCamera() { }
+        DrapingManager& _dm;
         const osg::Camera* _camera;
     };
 
@@ -133,9 +135,7 @@ namespace
 
     // Experimental.
     void optimizeProjectionMatrix(OverlayDecorator::TechRTTParams& params, double maxFarNearRatio)
-    {
-        LocalPerViewData& local = *static_cast<LocalPerViewData*>(params._techniqueData.get());
-        
+    {        
         // t0,t1,t2,t3 will form a polygon that tightly fits the
         // main camera's frustum. Texture near the camera will get
         // more resolution then texture far away.
@@ -346,56 +346,43 @@ DrapingTechnique::hasData(OverlayDecorator::TechRTTParams& params) const
     return getBound(params).valid();
 }
 
-#if OSG_MIN_VERSION_REQUIRED(3,4,0)
-
-// Customized texture class will disable texture filtering when rendering under a pick camera.
-class DrapingTexture : public osg::Texture2D
+namespace
 {
-public:
-    virtual void apply(osg::State& state) const {
-        osg::State::UniformMap::const_iterator i = state.getUniformMap().find("oe_isPickCamera");
-        bool isPickCamera = false;
-        if (i != state.getUniformMap().end())
+    // Customized texture class will disable texture filtering when rendering under a pick camera.
+    class DrapingTexture : public osg::Texture2D
+    {
+    public:
+        virtual void apply(osg::State& state) const
         {
-            if (!i->second.uniformVec.empty())
+            const osg::StateSet::DefineList& defines = state.getDefineMap().currentDefines;
+            if (defines.find("OE_IS_PICK_CAMERA") != defines.end())
             {
-                i->second.uniformVec.back().first->get(isPickCamera);
+                FilterMode minFilter = _min_filter;
+                FilterMode magFilter = _mag_filter;
+                DrapingTexture* ncThis = const_cast<DrapingTexture*>(this);
+                ncThis->_min_filter = NEAREST;
+                ncThis->_mag_filter = NEAREST;
+                ncThis->dirtyTextureParameters();
+                osg::Texture2D::apply(state);
+                ncThis->_min_filter = minFilter;
+                ncThis->_mag_filter = magFilter;
+                ncThis->dirtyTextureParameters();
+            }
+            else
+            {
+                osg::Texture2D::apply(state);
             }
         }
-
-        if (isPickCamera)
-        {
-            FilterMode minFilter = _min_filter;
-            FilterMode magFilter = _mag_filter;
-            DrapingTexture* ncThis = const_cast<DrapingTexture*>(this);
-            ncThis->_min_filter = NEAREST;
-            ncThis->_mag_filter = NEAREST;
-            ncThis->dirtyTextureParameters();
-            osg::Texture2D::apply(state);
-            ncThis->_min_filter = minFilter;
-            ncThis->_mag_filter = magFilter;
-            ncThis->dirtyTextureParameters();
-        }
-        else
-        {
-            osg::Texture2D::apply(state);
-        }
-    }
-};
-
-#endif
+    };
+}
 
 void
 DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
 {
-    // create the projected texture:
+    OE_INFO << LC << "Using texture size = " << _textureSize.get() << std::endl;
 
-#if OSG_MIN_VERSION_REQUIRED(3,4,0)
+    // create the projected texture:
     osg::Texture2D* projTexture = new DrapingTexture(); 
-#else 
-    osg::Texture2D* projTexture = new osg::Texture2D();
-    OE_WARN << LC << "RTT Picking of draped geometry may not work propertly under OSG < 3.4" << std::endl;
-#endif
 
     projTexture->setTextureSize( *_textureSize, *_textureSize );
     projTexture->setInternalFormat( GL_RGBA );
@@ -409,7 +396,7 @@ DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
     projTexture->setBorderColor( osg::Vec4(0,0,0,0) );
 
     // set up the RTT camera:
-    params._rttCamera = new DrapingCamera(); //new osg::Camera();
+    params._rttCamera = new DrapingCamera(_drapingManager);
     params._rttCamera->setClearColor( osg::Vec4f(0,0,0,0) );
     // this ref frame causes the RTT to inherit its viewpoint from above (in order to properly
     // process PagedLOD's etc. -- it doesn't affect the perspective of the RTT camera though)
@@ -430,7 +417,7 @@ DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
         // a PBUFFER_RTT impl
         if ( Registry::capabilities().supportsDepthPackedStencilBuffer() )
         {
-#ifdef OSG_GLES2_AVAILABLE 
+#if defined(OSG_GLES2_AVAILABLE) || defined(OSG_GLES3_AVAILABLE)
             params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH24_STENCIL8_EXT );
 #else
             params._rttCamera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, GL_DEPTH_STENCIL_EXT );
@@ -449,13 +436,15 @@ DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
         params._rttCamera->setClearMask( GL_COLOR_BUFFER_BIT );
     }
 
+
     // set up a StateSet for the RTT camera.
     osg::StateSet* rttStateSet = params._rttCamera->getOrCreateStateSet();
 
     osg::StateAttribute::OverrideValue forceOff =
         osg::StateAttribute::OFF | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE;
 
-    rttStateSet->addUniform( Registry::shaderFactory()->createUniformForGLMode(GL_LIGHTING, forceOff) );
+    rttStateSet->setDefine(OE_LIGHTING_DEFINE, forceOff);
+    //rttStateSet->addUniform( Registry::shaderFactory()->createUniformForGLMode(GL_LIGHTING, forceOff) );
     rttStateSet->setMode( GL_LIGHTING, forceOff );
     
     // activate blending within the RTT camera's FBO
@@ -489,7 +478,7 @@ DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
     // overlay geometry is rendered with no depth testing, and in the order it's found in the
     // scene graph... until further notice.
     rttStateSet->setMode(GL_DEPTH_TEST, 0);
-    rttStateSet->setBinName( "TraversalOrderBin" );
+    rttStateSet->setRenderBinDetails(1, "TraversalOrderBin", osg::StateSet::OVERRIDE_PROTECTED_RENDERBIN_DETAILS );
 
     // add to the terrain stateset, i.e. the stateset that the OverlayDecorator will
     // apply to the terrain before cull-traversing it. This will activate the projective
@@ -512,6 +501,7 @@ DrapingTechnique::setUpCamera(OverlayDecorator::TechRTTParams& params)
         // alpha. We shall see.
         const char* warpClip =
             "#version " GLSL_VERSION_STR "\n"
+            GLSL_DEFAULT_PRECISION_FLOAT "\n"
             "void oe_overlay_warpClip(inout vec4 vclip) { \n"
             "    if (vclip.z > 1.0) vclip.z = vclip.w+1.0; \n"
             "} \n";
@@ -579,13 +569,17 @@ DrapingTechnique::preCullTerrain(OverlayDecorator::TechRTTParams& params,
     if ( !params._rttCamera.valid() && _textureUnit.isSet() )
     {
         setUpCamera( params );
+
+        // We do this so we can detect the RTT's camera's parent for 
+        // things like auto-scaling, picking, and so on.
+        params._rttCamera->setView(cv->getCurrentCamera()->getView());
     }
 }
        
 const osg::BoundingSphere&
 DrapingTechnique::getBound(OverlayDecorator::TechRTTParams& params) const
 {
-    return DrapingCullSet::get(params._mainCamera).getBound();
+    return _drapingManager.get(params._mainCamera).getBound();
 }
 
 void
@@ -712,7 +706,6 @@ DrapingTechnique::onInstall( TerrainEngineNode* engine )
         unsigned maxSize = Registry::capabilities().getMaxFastTextureSize();
         _textureSize.init( osg::minimum( 2048u, maxSize ) );
     }
-    OE_INFO << LC << "Using texture size = " << *_textureSize << std::endl;
 }
 
 void
diff --git a/src/osgEarth/DrawInstanced b/src/osgEarth/DrawInstanced
index f0a863d..00e2a14 100644
--- a/src/osgEarth/DrawInstanced
+++ b/src/osgEarth/DrawInstanced
@@ -69,8 +69,8 @@ namespace osgEarth
 
         protected:
             unsigned _numInstances;
-            bool     _optimize;
-            osg::ref_ptr<osg::Drawable::ComputeBoundingBoxCallback> _staticBBoxCallback;
+            osg::BoundingBox _bbox;
+            bool _optimize;
             std::list<osg::PrimitiveSet*> _primitiveSets;
         };
 
diff --git a/src/osgEarth/DrawInstanced.cpp b/src/osgEarth/DrawInstanced.cpp
index 13e1316..d9e373d 100644
--- a/src/osgEarth/DrawInstanced.cpp
+++ b/src/osgEarth/DrawInstanced.cpp
@@ -74,7 +74,6 @@ namespace
             osg::Vec3d centerView = bbox.center() * ri.getState()->getModelViewMatrix();
             float rangeToBS = (float)-centerView.z() - radius;
 
-#if OSG_MIN_VERSION_REQUIRED(3,3,0)
             // check for inherit mode (3.3.0+ only)
             osg::Camera* cam = ri.getCurrentCamera();
 
@@ -90,7 +89,6 @@ namespace
                     rangeToBS = (float)(-centerRefView.z() - radius);
                 }
             }
-#endif
 
             // these should obviously be programmable
             const float maxDistance = 2000.0f;
@@ -127,28 +125,7 @@ namespace
     };
 
     typedef std::map< osg::ref_ptr<osg::Node>, std::vector<ModelInstance> > ModelInstanceMap;
-    
-    /**
-     * Simple bbox callback to return a static bbox.
-     */
-    struct StaticBoundingBox : public osg::Drawable::ComputeBoundingBoxCallback
-    {
-        osg::BoundingBox _bbox;
-        StaticBoundingBox( const osg::BoundingBox& bbox ) : _bbox(bbox) { }
-        osg::BoundingBox computeBound(const osg::Drawable&) const { return _bbox; }
-    };
 
-    // assume x is positive
-    static int nextPowerOf2(int x)
-    {
-        --x;
-        x |= x >> 1;
-        x |= x >> 2;
-        x |= x >> 4;
-        x |= x >> 8;
-        x |= x >> 16;
-        return x+1;
-    }
 }
 
 //----------------------------------------------------------------------
@@ -158,12 +135,11 @@ ConvertToDrawInstanced::ConvertToDrawInstanced(unsigned                numInstan
                                                const osg::BoundingBox& bbox,
                                                bool                    optimize ) :
 _numInstances    ( numInstances ),
+_bbox(bbox),
 _optimize        ( optimize )
 {
     setTraversalMode( TRAVERSE_ALL_CHILDREN );
     setNodeMaskOverride( ~0 );
-
-    _staticBBoxCallback = new StaticBoundingBox(bbox);
 }
 
 
@@ -182,11 +158,7 @@ ConvertToDrawInstanced::apply( osg::Geode& geode )
                 geom->setUseVertexBufferObjects( true );
             }
 
-            if ( _staticBBoxCallback.valid() )
-            {
-                geom->setComputeBoundingBoxCallback( _staticBBoxCallback.get() ); 
-                geom->dirtyBound();
-            }
+            geom->setInitialBound(_bbox);
 
             // convert to use DrawInstanced
             for( unsigned p=0; p<geom->getNumPrimitiveSets(); ++p )
@@ -278,7 +250,7 @@ DrawInstanced::convertGraphToUseDrawInstanced( osg::Group* parent )
     // place a static bounding sphere on the graph since we intend to alter
     // the structure of the subgraph.
     const osg::BoundingSphere& bs = parent->getBound();
-    parent->setComputeBoundingSphereCallback( new StaticBound(bs) );
+    parent->setInitialBound(bs);
     parent->dirtyBound();
 
     ModelInstanceMap models;
@@ -319,8 +291,9 @@ DrawInstanced::convertGraphToUseDrawInstanced( osg::Group* parent )
 	int maxTBOSize = Registry::capabilities().getMaxTextureBufferSize();
 	// This is the total number of instances it can store
 	// We will iterate below. If the number of instances is larger than the buffer can store
-	// we make more tbos
-	int maxTBOInstancesSize = maxTBOSize/4;// 4 vec4s per matrix.
+    // we make more tbos
+    int matrixSize = 4 * 4 * sizeof(float); // 4 vec4's.
+    int maxTBOInstancesSize = maxTBOSize / matrixSize;
 
     // For each model:
     for( ModelInstanceMap::iterator i = models.begin(); i != models.end(); ++i )
@@ -351,7 +324,7 @@ DrawInstanced::convertGraphToUseDrawInstanced( osg::Group* parent )
 
 		if (instances.size()<maxTBOInstancesSize)
 		{
-			tboSize = nextPowerOf2(instances.size());
+			tboSize = instances.size();
 			numInstancesToStore = instances.size();
 		}
 		else
diff --git a/src/osgEarth/ElevationLOD b/src/osgEarth/ElevationLOD
index c072941..a0b9362 100644
--- a/src/osgEarth/ElevationLOD
+++ b/src/osgEarth/ElevationLOD
@@ -74,6 +74,9 @@ namespace osgEarth
         optional<float>  _maxRange;
     };
 
+    // AltitudeLOD is a better name.
+    typedef ElevationLOD AltitudeLOD;
+
 } // namespace osgEarth
 
 #endif // OSGEARTH_ELEVATIONLOD_H
diff --git a/src/osgEarth/ElevationLayer b/src/osgEarth/ElevationLayer
index 487049c..3e17c23 100644
--- a/src/osgEarth/ElevationLayer
+++ b/src/osgEarth/ElevationLayer
@@ -32,14 +32,22 @@ namespace osgEarth
     {
     public:
         /** Constructs new elevation layer options. */
-        ElevationLayerOptions( const ConfigOptions& options =ConfigOptions() );
+        ElevationLayerOptions();
+        
+        /** Deserializes new elevation layer options. */
+        ElevationLayerOptions(const ConfigOptions& options);
+
+        // Constructs new options with a layer name
+        ElevationLayerOptions(const std::string& name);
 
         /** Constructs new elevation layer options, given the underlying driver options. */
-        ElevationLayerOptions( const std::string& name, const TileSourceOptions& driverOptions );
+        ElevationLayerOptions(const std::string& name, const TileSourceOptions& driverOptions);
 
         /** dtor */
         virtual ~ElevationLayerOptions() { }
 
+    public:
+
         optional<bool>& offset() { return _offset; }
         const optional<bool>& offset() const { return _offset; }
 
@@ -48,8 +56,7 @@ namespace osgEarth
         const optional<ElevationNoDataPolicy>& noDataPolicy() const { return _noDataPolicy; }
 
     public:
-        virtual Config getConfig() const { return getConfig(false); }
-        virtual Config getConfig( bool isolate ) const;
+        virtual Config getConfig() const;
         virtual void mergeConfig( const Config& conf );
         
     private:
@@ -59,30 +66,28 @@ namespace osgEarth
         optional<bool>                  _offset;
         optional<ElevationNoDataPolicy> _noDataPolicy;
     };
+    
 
-    //--------------------------------------------------------------------
-
-    /**
-     * Callback for receiving notification of property changes on an ElevationLayer.
-     */
     struct ElevationLayerCallback : public TerrainLayerCallback
     {
-        //TODO
+        //EMPTY
+        typedef void (ElevationLayerCallback::*MethodPtr)(class ElevationLayer*);
     };
 
-    typedef void (ElevationLayerCallback::*ElevationLayerCallbackMethodPtr)(class ElevationLayer* layer);
-
-    typedef std::list< osg::ref_ptr<ElevationLayerCallback> > ElevationLayerCallbackList;
-
-
-    //--------------------------------------------------------------------
-
     /**
      * A map terrain layer containing elevation grid heightfields.
      */
     class OSGEARTH_EXPORT ElevationLayer : public TerrainLayer
     {
     public:
+        META_Layer(osgEarth, ElevationLayer, ElevationLayerOptions);
+
+        /**
+         * Constructs a blank Elevation Layer;
+         * Use options() to set up before calling open or adding to a Map.
+         */
+        ElevationLayer();
+
         /**
          * Constructs a new elevation layer with the specified options. It expects
          * the layer options to contain a reference to the neccesary driver options.
@@ -98,66 +103,78 @@ namespace osgEarth
         /**
          * Constructs a new elevation layer with the specified layer options and with a custom
          * TileSource instance created by the user.
+         *
+         * Note: the ElevationLayerOptions contains a driver() member for configuring a 
+         * TileSource. But in this constructor, you are passing in an existing TileSource,
+         * and thus the driver() member in ElevationLayerOptions will not be used.
          */
         ElevationLayer( const ElevationLayerOptions& options, TileSource* tileSource );
 
         /** dtor */
         virtual ~ElevationLayer() { }
 
-        /** Gets the initialization options with which the layer was created. */
-        const ElevationLayerOptions& getElevationLayerOptions() const { return _runtimeOptions; }
-        virtual const TerrainLayerOptions& getTerrainLayerRuntimeOptions() const { return _runtimeOptions; }
-
-        /** Adds a property notification callback to this layer */
-        void addCallback( ElevationLayerCallback* cb );
-
-        /** Removes a property notification callback from this layer */
-        void removeCallback( ElevationLayerCallback* cb );
-
     public: // methods
+        
+        /**
+         * Creates a GeoHeightField for this layer that corresponds to the extents and LOD 
+         * in the specified TileKey. The returned HeightField will always match the geospatial
+         * extents of that TileKey.
+         *
+         * @param key TileKey for which to create a heightfield.
+         */
+        GeoHeightField createHeightField(const TileKey& key);
 
         /**
          * Creates a GeoHeightField for this layer that corresponds to the extents and LOD 
          * in the specified TileKey. The returned HeightField will always match the geospatial
          * extents of that TileKey.
+         *
+         * @param key TileKey for which to create a heightfield.
+         * @param progress Callback for tracking progress and cancelation
          */
-        virtual GeoHeightField createHeightField(
-            const TileKey&    key,
-            ProgressCallback* progress =0L );
+        GeoHeightField createHeightField(const TileKey& key, ProgressCallback* progress);
 
         /**
          * Whether this layer contains offsets instead of absolute heights
          */
-        bool isOffset() const {
-            return _runtimeOptions.offset() == true;
-        }
+        bool isOffset() const;
+
+    protected: // Layer
+
+        virtual void init();
 
     protected:
-        
-        // creates a geoHF directly from the tile source
-        osg::HeightField* createHeightFieldFromTileSource( 
-            const TileKey&    key, 
-            ProgressCallback* progress);
 
-        // assembles tiles from a layer that is not in the same profile as the map, and
-        // returns a single tile in the map's profile.
-        osg::HeightField* assembleHeightFieldFromTileSource(
-            const TileKey&     key,
-            ProgressCallback*  progress );
+        // ctor called by a subclass that owns the options structure
+        ElevationLayer(ElevationLayerOptions* optionsPtr);
+
+        //! Subclass can override this by calling setTileSourceExpected(false).
+        //! You can create your own normal map, but usually ElevationLayer
+        //! will do this for you. Only do it if you really need to create
+        //! one by hand (for example, if you are compositing elevaition layers).
+        virtual void createImplementation(
+            const TileKey& key,
+            osg::ref_ptr<osg::HeightField>& out_hf,
+            osg::ref_ptr<NormalMap>& out_normalMap,
+            ProgressCallback* progress);
         
     private:
-        ElevationLayerOptions _runtimeOptions;
-
-        ElevationLayerCallbackList _callbacks;
-        virtual void fireCallback( TerrainLayerCallbackMethodPtr method );
-        virtual void fireCallback( ElevationLayerCallbackMethodPtr method );
 
         mutable osg::ref_ptr<TileSource::HeightFieldOperation> _preCacheOp;
 
         TileSource::HeightFieldOperation* getOrCreatePreCacheOp();
         Threading::Mutex _mutex;
+        
+        // creates a geoHF directly from the tile source
+        osg::HeightField* createHeightFieldFromTileSource( 
+            const TileKey&    key, 
+            ProgressCallback* progress);
 
-        void init();
+        void assembleHeightField(
+            const TileKey& key,
+            osg::ref_ptr<osg::HeightField>& out_hf,
+            osg::ref_ptr<NormalMap>& out_normalMap,
+            ProgressCallback* progress);
     };
 
 
@@ -171,8 +188,9 @@ namespace osgEarth
          * Populates an existing height field (hf must already exist) with height
          * values from the elevation layers.
          */
-        bool populateHeightField(
+        bool populateHeightFieldAndNormalMap(
             osg::HeightField*      hf,
+            NormalMap*             normalMap,
             const TileKey&         key,
             const Profile*         haeProfile,
             ElevationInterpolation interpolation,
diff --git a/src/osgEarth/ElevationLayer.cpp b/src/osgEarth/ElevationLayer.cpp
index ff33a80..24d4c85 100644
--- a/src/osgEarth/ElevationLayer.cpp
+++ b/src/osgEarth/ElevationLayer.cpp
@@ -21,6 +21,8 @@
 #include <osgEarth/HeightFieldUtils>
 #include <osgEarth/Progress>
 #include <osgEarth/MemCache>
+#include <osgEarth/Metrics>
+#include <osgEarth/ImageUtils>
 #include <osg/Version>
 #include <iterator>
 
@@ -29,16 +31,34 @@ using namespace OpenThreads;
 
 #define LC "[ElevationLayer] \"" << getName() << "\" : "
 
+namespace osgEarth {
+    REGISTER_OSGEARTH_LAYER(elevation, osgEarth::ElevationLayer);
+}
+
 //------------------------------------------------------------------------
 
-ElevationLayerOptions::ElevationLayerOptions( const ConfigOptions& options ) :
+ElevationLayerOptions::ElevationLayerOptions() :
+TerrainLayerOptions()
+{
+    setDefaults();
+    fromConfig(_conf);
+}
+
+ElevationLayerOptions::ElevationLayerOptions(const ConfigOptions& options) :
 TerrainLayerOptions( options )
 {
     setDefaults();
     fromConfig( _conf );
 }
 
-ElevationLayerOptions::ElevationLayerOptions( const std::string& name, const TileSourceOptions& driverOptions ) :
+ElevationLayerOptions::ElevationLayerOptions(const std::string& name) :
+TerrainLayerOptions( name )
+{
+    setDefaults();
+    fromConfig( _conf );
+}
+
+ElevationLayerOptions::ElevationLayerOptions(const std::string& name, const TileSourceOptions& driverOptions) :
 TerrainLayerOptions( name, driverOptions )
 {
     setDefaults();
@@ -53,13 +73,18 @@ ElevationLayerOptions::setDefaults()
 }
 
 Config
-ElevationLayerOptions::getConfig( bool isolate ) const
+ElevationLayerOptions::getConfig() const
 {
-    Config conf = TerrainLayerOptions::getConfig( isolate );
-    conf.updateIfSet("offset", _offset);
-    conf.updateIfSet("nodata_policy", "default",     _noDataPolicy, NODATA_INTERPOLATE );
-    conf.updateIfSet("nodata_policy", "interpolate", _noDataPolicy, NODATA_INTERPOLATE );
-    conf.updateIfSet("nodata_policy", "msl",         _noDataPolicy, NODATA_MSL );
+    Config conf = TerrainLayerOptions::getConfig();
+    conf.key() = "elevation";
+
+    conf.set("offset", _offset);
+    conf.set("nodata_policy", "default",     _noDataPolicy, NODATA_INTERPOLATE );
+    conf.set("nodata_policy", "interpolate", _noDataPolicy, NODATA_INTERPOLATE );
+    conf.set("nodata_policy", "msl",         _noDataPolicy, NODATA_MSL );
+
+    //if (driver().isSet())
+    //    conf.set("driver", driver()->getDriver());
 
     return conf;
 }
@@ -87,11 +112,11 @@ namespace
     // Opeartion that replaces invalid heights with the NO_DATA_VALUE marker.
     struct NormalizeNoDataValues : public TileSource::HeightFieldOperation
     {
-        NormalizeNoDataValues(TileSource* source)
+        NormalizeNoDataValues(TerrainLayer* layer)
         {
-            _noDataValue   = source->getNoDataValue();
-            _minValidValue = source->getMinValidValue();
-            _maxValidValue = source->getMaxValidValue();
+            _noDataValue   = layer->getNoDataValue();
+            _minValidValue = layer->getMinValidValue();
+            _maxValidValue = layer->getMaxValidValue();
         }
 
         void operator()(osg::ref_ptr<osg::HeightField>& hf)
@@ -102,8 +127,9 @@ namespace
                 for(osg::FloatArray::iterator i = values->begin(); i != values->end(); ++i)
                 {
                     float& value = *i;
-                    if ( osg::equivalent(value, _noDataValue) || value < _minValidValue || value > _maxValidValue )
+                    if ( osg::isNaN(value) || osg::equivalent(value, _noDataValue) || value < _minValidValue || value > _maxValidValue )
                     {
+                        OE_DEBUG << "Replaced " << value << " with NO_DATA_VALUE" << std::endl;
                         value = NO_DATA_VALUE;
                     }
                 } 
@@ -124,8 +150,8 @@ namespace
             return false;
         if (hf->getHeightList().size() != hf->getNumColumns() * hf->getNumRows())
             return false;
-        if (hf->getXInterval() < 1e-5 || hf->getYInterval() < 1e-5)
-            return false;
+        //if (hf->getXInterval() < 1e-5 || hf->getYInterval() < 1e-5)
+        //    return false;
         
         return true;
     }    
@@ -133,87 +159,91 @@ namespace
 
 //------------------------------------------------------------------------
 
-ElevationLayer::ElevationLayer( const ElevationLayerOptions& options ) :
-TerrainLayer   ( options, &_runtimeOptions ),
-_runtimeOptions( options )
+ElevationLayer::ElevationLayer() :
+TerrainLayer(&_optionsConcrete),
+_options(&_optionsConcrete)
 {
     init();
 }
 
-ElevationLayer::ElevationLayer( const std::string& name, const TileSourceOptions& driverOptions ) :
-TerrainLayer   ( ElevationLayerOptions(name, driverOptions), &_runtimeOptions ),
-_runtimeOptions( ElevationLayerOptions(name, driverOptions) )
+ElevationLayer::ElevationLayer(const ElevationLayerOptions& options) :
+TerrainLayer(&_optionsConcrete),
+_options(&_optionsConcrete),
+_optionsConcrete(options)
 {
     init();
 }
 
-ElevationLayer::ElevationLayer( const ElevationLayerOptions& options, TileSource* tileSource ) :
-TerrainLayer   ( options, &_runtimeOptions, tileSource ),
-_runtimeOptions( options )
+ElevationLayer::ElevationLayer(const std::string& name, const TileSourceOptions& driverOptions) :
+TerrainLayer(&_optionsConcrete),
+_options(&_optionsConcrete),
+_optionsConcrete(name, driverOptions)
 {
     init();
 }
 
-void
-ElevationLayer::init()
+ElevationLayer::ElevationLayer(const ElevationLayerOptions& options, TileSource* tileSource) :
+TerrainLayer(&_optionsConcrete, tileSource),
+_options(&_optionsConcrete),
+_optionsConcrete(options)
 {
-    TerrainLayer::init();
+    init();
 }
 
-void
-ElevationLayer::addCallback( ElevationLayerCallback* cb )
+ElevationLayer::ElevationLayer(ElevationLayerOptions* optionsPtr) :
+TerrainLayer(optionsPtr? optionsPtr : &_optionsConcrete),
+_options(optionsPtr? optionsPtr : &_optionsConcrete)
 {
-    _callbacks.push_back( cb );
+    //init(); // will be called by subclass.
 }
 
 void
-ElevationLayer::removeCallback( ElevationLayerCallback* cb )
+ElevationLayer::init()
 {
-    ElevationLayerCallbackList::iterator i = std::find( _callbacks.begin(), _callbacks.end(), cb );
-    if ( i != _callbacks.end() ) 
-        _callbacks.erase( i );
-}
+    TerrainLayer::init();
 
-void
-ElevationLayer::fireCallback( TerrainLayerCallbackMethodPtr method )
-{
-    for( ElevationLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
-    {
-        ElevationLayerCallback* cb = i->get();
-        (cb->*method)( this );
-    }
+    // elevation layers do not render directly; rather, a composite of elevation data
+    // feeds the terrain engine to permute the mesh.
+    setRenderType(RENDERTYPE_NONE);
 }
 
-void
-ElevationLayer::fireCallback( ElevationLayerCallbackMethodPtr method )
+bool
+ElevationLayer::isOffset() const
 {
-    for( ElevationLayerCallbackList::const_iterator i = _callbacks.begin(); i != _callbacks.end(); ++i )
-    {
-        ElevationLayerCallback* cb = i->get();
-        (cb->*method)( this );
-    }
+    return options().offset().get();
 }
 
 TileSource::HeightFieldOperation*
 ElevationLayer::getOrCreatePreCacheOp()
 {
-    if ( !_preCacheOp.valid() && getTileSource() )
+    if ( !_preCacheOp.valid() )
     {
         Threading::ScopedMutexLock lock(_mutex);
         if ( !_preCacheOp.valid() )
         {
-            _preCacheOp = new NormalizeNoDataValues( getTileSource() );
+            _preCacheOp = new NormalizeNoDataValues(this);
         }
     }
     return _preCacheOp.get();
 }
 
+void
+ElevationLayer::createImplementation(const TileKey& key,
+                                     osg::ref_ptr<osg::HeightField>& out_hf,
+                                     osg::ref_ptr<NormalMap>& out_normalMap,
+                                     ProgressCallback* progress)
+{
+    out_hf = createHeightFieldFromTileSource(key, progress);
+    
+    // Do not create a normal map here. The populateHeightField method will
+    // create the normal map.
+}
 
 osg::HeightField*
 ElevationLayer::createHeightFieldFromTileSource(const TileKey&    key,
                                                 ProgressCallback* progress)
 {
-    osg::HeightField* result = 0L;
+    osg::ref_ptr<osg::HeightField> result;
 
     TileSource* source = getTileSource();
     if ( !source )
@@ -231,7 +261,7 @@ ElevationLayer::createHeightFieldFromTileSource(const TileKey&    key,
     if ( key.getProfile()->isHorizEquivalentTo( getProfile() ) )
     {
         // Only try to get data if the source actually has data
-        if ( !source->hasData(key) )
+        if (!mayHaveData(key))
         {
             OE_DEBUG << LC << "Source for layer has no data at " << key.str() << std::endl;
             return 0L;
@@ -242,7 +272,7 @@ ElevationLayer::createHeightFieldFromTileSource(const TileKey&    key,
    
         // If the result is good, we how have a heightfield but it's vertical values
         // are still relative to the tile source's vertical datum. Convert them.
-        if ( result )
+        if (result.valid())
         {
             if ( ! key.getExtent().getSRS()->isVertEquivalentTo( getProfile()->getSRS() ) )
             {
@@ -250,13 +280,13 @@ ElevationLayer::createHeightFieldFromTileSource(const TileKey&    key,
                     getProfile()->getSRS()->getVerticalDatum(),    // from
                     key.getExtent().getSRS()->getVerticalDatum(),  // to
                     key.getExtent(),
-                    result );
+                    result.get() );
             }
         }
         
         // Blacklist the tile if it is the same projection as the source and
         // we can't get it and it wasn't cancelled
-        if (result == 0L)
+        if (!result.valid())
         {
             if ( progress == 0L ||
                  ( !progress->isCanceled() && !progress->needsRetry() ) )
@@ -270,18 +300,21 @@ ElevationLayer::createHeightFieldFromTileSource(const TileKey&    key,
     else
     {
         // note: this method takes care of the vertical datum shift internally.
-        result = assembleHeightFieldFromTileSource( key, progress );
+        osg::ref_ptr<NormalMap> dummyNormalMap;
+        assembleHeightField( key, result, dummyNormalMap, progress );
     }
 
-    return result;
+    return result.release();
 }
 
 
-osg::HeightField*
-ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
-                                                  ProgressCallback* progress)
+void
+ElevationLayer::assembleHeightField(const TileKey& key,
+                                    osg::ref_ptr<osg::HeightField>& out_hf,
+                                    osg::ref_ptr<NormalMap>& out_normalMap,
+                                    ProgressCallback* progress)
 {			
-    osg::HeightField* result = 0L;
+    //osg::HeightField* result = 0L;
 
     // Collect the heightfields for each of the intersecting tiles.
     GeoHeightFieldVector heightFields;
@@ -299,12 +332,15 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
         {
             const TileKey& layerKey = intersectingTiles[i];
 
-            if ( isKeyInRange(layerKey) )
+            if ( isKeyInLegalRange(layerKey) )
             {
-                osg::HeightField* hf = createHeightFieldFromTileSource( layerKey, progress );
-                if ( hf )
+                osg::ref_ptr<osg::HeightField> hf;
+                osg::ref_ptr<NormalMap> normalMap;
+                createImplementation(layerKey, hf, normalMap, progress);
+                //osg::HeightField* hf = createHeightFieldImplementation( layerKey, progress );
+                if (hf.valid())
                 {
-                    heightFields.push_back( GeoHeightField(hf, layerKey.getExtent()) );
+                    heightFields.push_back( GeoHeightField(hf.get(), normalMap.get(), layerKey.getExtent()) );
                 }
             }
         }
@@ -327,8 +363,10 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
         //Now sort the heightfields by resolution to make sure we're sampling the highest resolution one first.
         std::sort( heightFields.begin(), heightFields.end(), GeoHeightField::SortByResolutionFunctor());        
 
-        result = new osg::HeightField();
-        result->allocate(width, height);
+        out_hf = new osg::HeightField();
+        out_hf->allocate(width, height);
+
+        out_normalMap = new NormalMap(width, height);
 
         //Go ahead and set up the heightfield so we don't have to worry about it later
         double minx, miny, maxx, maxy;
@@ -346,30 +384,42 @@ ElevationLayer::assembleHeightFieldFromTileSource(const TileKey&    key,
 
                 //For each sample point, try each heightfield.  The first one with a valid elevation wins.
                 float elevation = NO_DATA_VALUE;
+                osg::Vec3 normal(0,0,1);
+
                 for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
                 {
                     // get the elevation value, at the same time transforming it vertically into the 
                     // requesting key's vertical datum.
                     float e = 0.0;
-                    if (itr->getElevation(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e))
+                    osg::Vec3 n;
+                    if (itr->getElevationAndNormal(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e, n))
                     {
                         elevation = e;
+                        normal = n;
                         break;
                     }
                 }
-                result->setHeight( c, r, elevation );                
+                out_hf->setHeight( c, r, elevation );   
+                out_normalMap->set( c, r, normal );
             }
         }
     }
-
-    return result;
 }
 
+GeoHeightField
+ElevationLayer::createHeightField(const TileKey& key)
+{
+    return createHeightField(key, 0L);
+}
 
 GeoHeightField
 ElevationLayer::createHeightField(const TileKey&    key,
                                   ProgressCallback* progress )
 {
+    METRIC_SCOPED_EX("ElevationLayer::createHeightField", 2,
+                     "key", key.str().c_str(),
+                     "name", getName().c_str());
+
     if (getStatus().isError())
     {
         return GeoHeightField::INVALID;
@@ -383,6 +433,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
 
     GeoHeightField result;
     osg::ref_ptr<osg::HeightField> hf;
+    osg::ref_ptr<NormalMap> normalMap;
 
     // Check the memory cache first
     bool fromMemCache = false;
@@ -410,8 +461,16 @@ ElevationLayer::createHeightField(const TileKey&    key,
         // See if there's a persistent cache.
         CacheBin* cacheBin = getCacheBin( key.getProfile() );
 
-        // validate that we have either a valid tile source, or we're cache-only.
-        if ( ! (getTileSource() || (policy.isCacheOnly() && cacheBin) ) )
+        // Can we continue? Only if either:
+        //  a) there is a valid tile source plugin;
+        //  b) a tile source is not expected, meaning the subclass overrides getHeightField; or
+        //  c) we are in cache-only mode and there is a valid cache bin.
+        bool canContinue =
+            getTileSource() ||
+            !isTileSourceExpected() ||
+            (policy.isCacheOnly() && cacheBin != 0L);
+
+        if (!canContinue)
         {
             disable("Error: layer does not have a valid TileSource, cannot create heightfield");
             return GeoHeightField::INVALID;
@@ -420,7 +479,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
         // validate the existance of a valid layer profile.
         if ( !policy.isCacheOnly() && !getProfile() )
         {
-            disable("Could not establish a valid profile");
+            disable("Could not establish a valid profile.. did you set one?");
             return GeoHeightField::INVALID;
         }
 
@@ -437,7 +496,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
             {            
                 bool expired = policy.isExpired(r.lastModifiedTime());
                 cachedHF = r.get<osg::HeightField>();
-                if ( cachedHF && validateHeightField(cachedHF) )
+                if ( cachedHF && validateHeightField(cachedHF.get()) )
                 {
                     if (!expired)
                     {
@@ -456,15 +515,27 @@ ElevationLayer::createHeightField(const TileKey&    key,
 
         if ( !hf.valid() )
         {
-            // bad tilesource? fail
-            if ( !getTileSource() || !getTileSource()->isOK() )
+            if ( !isKeyInLegalRange(key) )
                 return GeoHeightField::INVALID;
 
-            if ( !isKeyInRange(key) )
-                return GeoHeightField::INVALID;
+            // If no tile source is expected, create a height field by calling
+            // the raw inheritable method.
+            if (!isTileSourceExpected())
+            {
+                createImplementation(key, hf, normalMap, progress);
+                //hf = createHeightFieldImplementation(key, progress);
+            }
 
-            // build a HF from the TileSource.
-            hf = createHeightFieldFromTileSource( key, progress );
+            else
+            {
+                // bad tilesource? fail
+                if ( !getTileSource() || !getTileSource()->isOK() )
+                    return GeoHeightField::INVALID;
+
+                // build a HF from the TileSource.
+                //hf = createHeightFieldImplementation( key, progress );
+                createImplementation(key, hf, normalMap, progress);
+            }
 
             // validate it to make sure it's legal.
             if ( hf.valid() && !validateHeightField(hf.get()) )
@@ -479,7 +550,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
                  !fromCache    &&
                  policy.isCacheWriteable() )
             {
-                cacheBin->write(cacheKey, hf, 0L);
+                cacheBin->write(cacheKey, hf.get(), 0L);
             }
 
             // We have an expired heightfield from the cache and no new data from the TileSource.  So just return the cached data.
@@ -507,7 +578,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
 
         if ( hf.valid() )
         {
-            result = GeoHeightField( hf.get(), key.getExtent() );
+            result = GeoHeightField( hf.get(), normalMap.get(), key.getExtent() );
         }
     }
 
@@ -521,7 +592,7 @@ ElevationLayer::createHeightField(const TileKey&    key,
     // post-processing:
     if ( result.valid() )
     {
-        if ( _runtimeOptions.noDataPolicy() == NODATA_MSL )
+        if ( options().noDataPolicy() == NODATA_MSL )
         {
             // requested VDatum:
             const VerticalDatum* outputVDatum = key.getExtent().getSRS()->getVerticalDatum();
@@ -571,21 +642,145 @@ osg::MixinVector< osg::ref_ptr<ElevationLayer> >( rhs )
 namespace
 {
     typedef osg::ref_ptr<ElevationLayer>          RefElevationLayer;
-    typedef std::pair<RefElevationLayer, TileKey> LayerAndKey;
-    typedef std::vector<LayerAndKey>              LayerAndKeyVector;
+    struct LayerData {
+        RefElevationLayer layer;
+        TileKey key;
+        int index;
+    };
+    //typedef std::pair<RefElevationLayer, TileKey> LayerAndKey;
+    typedef std::vector<LayerData>              LayerDataVector;
+
+    //! Gets the normal vector for elevation data at column s, row t.
+    osg::Vec3 getNormal(const GeoExtent& extent, const osg::HeightField* hf, int s, int t)
+    {
+        int w = hf->getNumColumns();
+        int h = hf->getNumRows();
+
+        osg::Vec2d res(
+            extent.width() / (double)(w-1),
+            extent.height() / (double)(h-1));
+
+        float e = hf->getHeight(s, t);
+
+        double dx = res.x(), dy = res.y();
+
+        if (extent.getSRS()->isGeographic())
+        {
+            double R = extent.getSRS()->getEllipsoid()->getRadiusEquator();
+            double mPerDegAtEquator = (2.0 * osg::PI * R) / 360.0;
+            dy = dy * mPerDegAtEquator;
+            double lat = extent.yMin() + res.y()*(double)t;
+            dx = dx * mPerDegAtEquator * cos(osg::DegreesToRadians(lat));
+        }
+        
+        osg::Vec3d west(0, 0, e), east(0, 0, e), south(0, 0, e), north(0, 0, e);
+
+        if (s > 0)     west.set (-dx, 0, hf->getHeight(s-1, t));
+        if (s < w - 1) east.set ( dx, 0, hf->getHeight(s+1, t));
+        if (t > 0)     south.set(0, -dy, hf->getHeight(s, t-1));
+        if (t < h - 1) north.set(0,  dy, hf->getHeight(s, t+1));
+
+        osg::Vec3d normal = (east - west) ^ (north - south);
+        return normal;
+    }
+
+    //! Creates a normal map for heightfield "hf" and stores it in the
+    //! pre-allocated NormalMap.
+    //!
+    //! "deltaLOD" holds the difference in LODs between the heightfield itself and the LOD
+    //! from which the elevation value came. This will be positive when we had to "fall back" on 
+    //! lower LOD data to fetch an elevation value. When this happens we need to interpolate
+    //! between "real" normals instead of sampling them from the neighboring pixels, otherwise
+    //! ugly faceting will occur.
+    //!
+    //! Unfortunately, it there's an offset layer, this will update the deltaLOD and we will 
+    //! get faceting if the real elevation layer is from a lower LOD. The only solution to this
+    //! would be to sample the elevation data using a spline function instead of bilinear
+    //! interpolation -- but we would need to do that to a separate heightfield (especially for
+    //! normals) in order to maintain terrain correlation. Maybe someday.
+    void createNormalMap(const GeoExtent& extent, const osg::HeightField* hf, const osg::ShortArray* deltaLOD, NormalMap* normalMap)
+    {
+        int w = hf->getNumColumns();
+        int h = hf->getNumRows();
+
+        for (int t = 0; t < (int)hf->getNumRows(); ++t)
+        {
+            for (int s = 0; s<(int)hf->getNumColumns(); ++s)
+            {
+                int step = 1 << (*deltaLOD)[t*h + s];
+
+                osg::Vec3 normal;
+
+                // four corners:
+                int s0=s, s1=s, t0=t, t1=t;
+
+                if (step == 1)
+                {
+                    // Same LOD, simple query
+                    normal = getNormal(extent, hf, s, t);
+                }
+                else
+                {
+                    int s0 = std::max(s - (s % step), 0);
+                    int s1 = (s%step == 0)? s0 : std::min(s0+step, w-1);
+                    int t0 = std::max(t - (t % step), 0);
+                    int t1 = (t%step == 0)? t0 : std::min(t0+step, h-1);
+                    
+                    if (s0 == s1 && t0 == t1)
+                    {
+                        // on-pixel, simple query
+                        normal = getNormal(extent, hf, s0, t0);
+                    }
+                    else if (s0 == s1)
+                    {
+                        // same column; linear interpolate along row
+                        osg::Vec3 S = getNormal(extent, hf, s0, t0);
+                        osg::Vec3 N = getNormal(extent, hf, s0, t1);
+                        normal = S*(double)(t1 - t) + N*(double)(t - t0);
+                    }
+                    else if (t0 == t1)
+                    {
+                        // same row; linear interpolate along column
+                        osg::Vec3 W = getNormal(extent, hf, s0, t0);
+                        osg::Vec3 E = getNormal(extent, hf, s1, t0);
+                        normal = W*(double)(s1 - s) + E*(double)(s - s0);
+                    }
+                    else
+                    {
+                        // bilinear interpolate
+                        osg::Vec3 SW = getNormal(extent, hf, s0, t0);
+                        osg::Vec3 SE = getNormal(extent, hf, s1, t0);
+                        osg::Vec3 NW = getNormal(extent, hf, s0, t1);
+                        osg::Vec3 NE = getNormal(extent, hf, s1, t1);
+
+                        osg::Vec3 S = SW*(double)(s1 - s) + SE*(double)(s - s0);
+                        osg::Vec3 N = NW*(double)(s1 - s) + NE*(double)(s - s0);
+                        normal = S*(double)(t1 - t) + N*(double)(t - t0);
+                    }
+                }
+
+                normal.normalize();
+
+                normalMap->set(s, t, normal, 0.0f);
+            }
+        }
+    }
 }
 
 bool
-ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
-                                          const TileKey&         key,
-                                          const Profile*         haeProfile,
-                                          ElevationInterpolation interpolation,
-                                          ProgressCallback*      progress ) const
+ElevationLayerVector::populateHeightFieldAndNormalMap(osg::HeightField*      hf,
+                                                      NormalMap*             normalMap,
+                                                      const TileKey&         key,
+                                                      const Profile*         haeProfile,
+                                                      ElevationInterpolation interpolation,
+                                                      ProgressCallback*      progress ) const
 {
     // heightfield must already exist.
     if ( !hf )
         return false;
 
+    METRIC_SCOPED("ElevationLayer.populateHeightField");
+
     // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if
     // the map profile has a vertical datum. This is the usual case when building the 3D
     // terrain, for example. Construct a temporary key that doesn't have the vertical
@@ -597,16 +792,17 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
     }
     
     // Collect the valid layers for this tile.
-    LayerAndKeyVector contenders;
-    LayerAndKeyVector offsets;
+    LayerDataVector contenders;
+    LayerDataVector offsets;
 
     // Track the number of layers that would return fallback data.
     unsigned numFallbackLayers = 0;
 
     // Check them in reverse order since the highest priority is last.
-    for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
+    for (int i = size()-1; i>=0; --i)
+    //for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
     {
-        ElevationLayer* layer = i->get();
+        ElevationLayer* layer = (*this)[i].get(); //i->get();
 
         if ( layer->getEnabled() && layer->getVisible() )
         {
@@ -618,44 +814,48 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
             bool useLayer = true;
             TileKey bestKey( mappedKey );
 
-            // Is there a tilesource? If not we are cache-only and cannot reject the layer.
-            if ( layer->getTileSource() )
+            // Check whether the non-mapped key is valid according to the user's min/max level settings:
+            if ( !layer->isKeyInLegalRange(key) )
             {
-                // Check whether the non-mapped key is valid according to the user's min/max level settings:
-                if ( !layer->isKeyInRange(key) )
-                {
-                    useLayer = false;
-                }
+                useLayer = false;
+            }
                 
-
-                // Find the "best available" mapped key from the tile source:
-                else 
+            // Find the "best available" mapped key from the tile source:
+            else 
+            {
+                bestKey = layer->getBestAvailableTileKey(mappedKey);
+                if (bestKey.valid())
                 {
-                    if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) )
+                    // If the bestKey is not the mappedKey, this layer is providing
+                    // fallback data (data at a lower resolution than requested)
+                    if ( mappedKey != bestKey )
                     {
-                        // If the bestKey is not the mappedKey, this layer is providing
-                        // fallback data (data at a lower resolution than requested)
-                        if ( mappedKey != bestKey )
-                        {
-                            numFallbackLayers++;
-                        }
-                    }
-                    else
-                    {
-                        useLayer = false;
+                        numFallbackLayers++;
                     }
                 }
+                else
+                {
+                    useLayer = false;
+                }
             }
 
             if ( useLayer )
             {
                 if ( layer->isOffset() )
                 {
-                    offsets.push_back( std::make_pair(layer, bestKey) );
+                    offsets.push_back(LayerData());
+                    LayerData& ld = offsets.back();
+                    ld.layer = layer;
+                    ld.key = bestKey;
+                    ld.index = i;
                 }
                 else
                 {
-                    contenders.push_back( std::make_pair(layer, bestKey) );
+                    contenders.push_back(LayerData());
+                    LayerData& ld = contenders.back();
+                    ld.layer = layer;
+                    ld.key = bestKey;
+                    ld.index = i;
                 }
             }
         }
@@ -680,13 +880,44 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
     double   ymin       = key.getExtent().yMin();
     double   dx         = key.getExtent().width() / (double)(numColumns-1);
     double   dy         = key.getExtent().height() / (double)(numRows-1);
+
+#if 0
+    // If the incoming heightfield requests a positive border width, 
+    // we need to adjust the extents so that we request data outside the
+    // extent of the tile key:
+    unsigned border = hf->getBorderWidth();
+    if (border > 0u)
+    {
+        dx = key.getExtent().width() / (double)(numColumns - (border*2+1));
+        dy = key.getExtent().height() / (double)(numRows - (border*2+1));
+        xmin -= dx * (double)border;
+        ymin -= dy * (double)border;
+    }
+#endif
     
     // We will load the actual heightfields on demand. We might not need them all.
+#if 0
     GeoHeightFieldVector heightFields(contenders.size());
     GeoHeightFieldVector offsetFields(offsets.size());
     std::vector<bool>    heightFallback(contenders.size(), false);
     std::vector<bool>    heightFailed(contenders.size(), false);
     std::vector<bool>    offsetFailed(offsets.size(), false);
+#else
+    GeoHeightFieldVector heightFields[9];
+    GeoHeightFieldVector offsetFields[9]; //(offsets.size());
+    std::vector<bool>    heightFallback[9]; //(contenders.size(), false);
+    std::vector<bool>    heightFailed[9]; //(contenders.size(), false);
+    std::vector<bool>    offsetFailed[9]; //(offsets.size(), false);
+
+    for (int n = 0; n < 9; ++n)
+    {
+        heightFields[n].resize(contenders.size());
+        offsetFields[n].resize(offsets.size());
+        heightFallback[n].assign(9, false);
+        heightFailed[n].assign(9, false);
+        offsetFailed[n].assign(9, false);
+    }
+#endif
 
     // The maximum number of heightfields to keep in this local cache
     unsigned int maxHeightFields = 50;
@@ -697,9 +928,14 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
     bool realData = false;
 
     unsigned int total = numColumns * numRows;
-    unsigned int completed = 0;
+
+    // query resolution interval (x, y) of each sample.
+    osg::ref_ptr<osg::ShortArray> deltaLOD = new osg::ShortArray(total);
+    
     int nodataCount = 0;
 
+    TileKey scratchKey; // Storage if a new key needs to be constructed
+
     for (unsigned c = 0; c < numColumns; ++c)
     {
         double x = xmin + (dx * (double)c);
@@ -708,67 +944,100 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
             double y = ymin + (dy * (double)r);
 
             // Collect elevations from each layer as necessary.
-            bool resolved = false;
+            int resolvedIndex = -1;
+
+            osg::Vec3 normal_sum(0,0,0);
 
-            for(int i=0; i<contenders.size() && !resolved; ++i)
+            for(int i=0; i<contenders.size() && resolvedIndex<0; ++i)
             {
-                if ( heightFailed[i] )
+                ElevationLayer* layer = contenders[i].layer.get();                
+                TileKey& contenderKey = contenders[i].key;
+                int index = contenders[i].index;
+
+                // If there is a border, the edge points may not fall within the key extents 
+                // and we may need to fetch a neighboring key.
+
+                int n = 4; // index 4 is the center/default tile
+
+#if 0
+                if (border > 0u && !contenderKey.getExtent().contains(x, y))
+                {
+                    int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
+                    int dTy = y < contenderKey.getExtent().yMin() ? +1 : y > contenderKey.getExtent().yMax() ? -1 : 0;
+                    contenderKey = contenderKey.createNeighborKey(dTx, dTy);
+                    n = (dTy+1)*3 + (dTx+1);
+                }
+#endif
+
+                if ( heightFailed[n][i] )
                     continue;
 
-                ElevationLayer* layer = contenders[i].first.get();
+                TileKey* actualKey = &contenderKey;
 
-                GeoHeightField& layerHF = heightFields[i];
-                TileKey actualKey = contenders[i].second;
+                GeoHeightField& layerHF = heightFields[n][i];
 
                 if (!layerHF.valid())
                 {
                     // We couldn't get the heightfield from the cache, so try to create it.
                     // We also fallback on parent layers to make sure that we have data at the location even if it's fallback.
-                    while (!layerHF.valid() && actualKey.valid())
+                    while (!layerHF.valid() && actualKey->valid() && layer->isKeyInLegalRange(*actualKey))
                     {
-                        layerHF = layer->createHeightField(actualKey, progress);
+                        layerHF = layer->createHeightField(*actualKey, progress);
                         if (!layerHF.valid())
                         {
-                            actualKey = actualKey.createParentKey();
+                            if (actualKey != &scratchKey)
+                            {
+                                scratchKey = *actualKey;
+                                actualKey = &scratchKey;
+                            }
+                            *actualKey = actualKey->createParentKey();
                         }
                     }
 
                     // Mark this layer as fallback if necessary.
                     if (layerHF.valid())
                     {
-                        heightFallback[i] = actualKey != contenders[i].second;
+                        heightFallback[n][i] = (*actualKey != contenderKey); // actualKey != contenders[i].second;
                         numHeightFieldsInCache++;
                     }
                     else
                     {
-                        heightFailed[i] = true;
+                        heightFailed[n][i] = true;
                         continue;
                     }
                 }
 
                 if (layerHF.valid())
                 {
-                    bool isFallback = heightFallback[i];
+                    bool isFallback = heightFallback[n][i];
 
                     // We only have real data if this is not a fallback heightfield.
                     if (!isFallback)
                     {
                         realData = true;
                     }
-
+                    
                     float elevation;
                     if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation))
                     {
                         if ( elevation != NO_DATA_VALUE )
                         {
-                            resolved = true;                    
+                            // remember the index so we can only apply offset layers that
+                            // sit on TOP of this layer.
+                            resolvedIndex = index;
+
                             hf->setHeight(c, r, elevation);
+
+                            if (deltaLOD)
+                            {
+                                (*deltaLOD)[r*numColumns + c] = key.getLOD() - actualKey->getLOD();
+                            }
                         }
                         else
                         {
                             ++nodataCount;
                         }
-                    }
+                    }                    
                 }
 
 
@@ -776,10 +1045,13 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                 if (numHeightFieldsInCache >= maxHeightFields)
                 {
                     //OE_NOTICE << "Clearing cache" << std::endl;
-                    for (unsigned int k = 0; k < heightFields.size(); k++)
+                    for (unsigned int j = 0; j < 9; ++j)
                     {
-                        heightFields[k] = GeoHeightField::INVALID;
-                        heightFallback[k] = false;
+                        for (unsigned int k = 0; k < heightFields[j].size(); k++)
+                        {
+                            heightFields[j][k] = GeoHeightField::INVALID;
+                            heightFallback[j][k] = false;
+                        }
                     }
                     numHeightFieldsInCache = 0;
                 }
@@ -787,18 +1059,40 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
 
             for(int i=offsets.size()-1; i>=0; --i)
             {
-                if ( offsetFailed[i] )
+                // Only apply an offset layer if it sits on top of the resolved layer
+                // (or if there was no resolved layer).
+                if (resolvedIndex >= 0 && offsets[i].index < resolvedIndex)
                     continue;
 
-                GeoHeightField& layerHF = offsetFields[i];
+                TileKey &contenderKey = offsets[i].key;
+
+                // If there is a border, the edge points may not fall within the key extents 
+                // and we may need to fetch a neighboring key.
+
+                int n = 4; // index 4 is the center/default tile
+
+#if 0
+                if (border > 0u && !contenderKey.getExtent().contains(x, y))
+                {
+                    int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
+                    int dTy = y < contenderKey.getExtent().yMin() ? +1 : x > contenderKey.getExtent().yMax() ? -1 : 0;
+                    contenderKey = contenderKey.createNeighborKey(dTx, dTy);
+                    n = (dTy+1)*3 + (dTx+1);
+                }
+#endif
+                
+                if ( offsetFailed[n][i] == true )
+                    continue;
+
+                GeoHeightField& layerHF = offsetFields[n][i];
                 if ( !layerHF.valid() )
                 {
-                    ElevationLayer* offset = offsets[i].first.get();
+                    ElevationLayer* offset = offsets[i].layer.get();
 
-                    layerHF = offset->createHeightField(offsets[i].second, progress);
+                    layerHF = offset->createHeightField(contenderKey, progress);
                     if ( !layerHF.valid() )
                     {
-                        offsetFailed[i] = true;
+                        offsetFailed[n][i] = true;
                         continue;
                     }
                 }
@@ -811,11 +1105,24 @@ ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                     elevation != NO_DATA_VALUE)
                 {                    
                     hf->getHeight(c, r) += elevation;
+
+                    // Update the resolution tracker to account for the offset. Sadly this
+                    // will wipe out the resolution of the actual data, and might result in 
+                    // normal faceting. See the comments on "createNormalMap" for more info
+                    if (deltaLOD)
+                    {
+                        (*deltaLOD)[r*numColumns + c] = key.getLOD() - contenderKey.getLOD();
+                    }
                 }
             }
         }
     }
 
+    if (normalMap)
+    {
+        createNormalMap(key.getExtent(), hf, deltaLOD.get(), normalMap);
+    }
+
     // Return whether or not we actually read any real data
     return realData;
 }
diff --git a/src/osgEarth/ElevationPool b/src/osgEarth/ElevationPool
new file mode 100644
index 0000000..d3173ef
--- /dev/null
+++ b/src/osgEarth/ElevationPool
@@ -0,0 +1,266 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2016 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#ifndef OSGEARTH_ELEVATION_POOL_H
+#define OSGEARTH_ELEVATION_POOL_H
+
+#include <osgEarth/Common>
+#include <osgEarth/ElevationLayer>
+#include <osgEarth/MapFrame>
+#include <osgEarth/GeoData>
+#include <osgEarth/TileKey>
+#include <osgEarth/ThreadingUtils>
+#include <osg/Timer>
+#include <map>
+
+namespace osgEarth
+{
+    using namespace Threading;
+
+    // defined at the end of this file
+    class ElevationEnvelope;
+    class Map;
+
+    //! Result of an elevation query.
+    struct ElevationSample : public osg::Referenced
+    {
+        float elevation;
+        float resolution;
+        ElevationSample(float a, float b) : elevation(a), resolution(b) { }
+    };
+
+    /**
+     * A pool of elevation data that can be used to manage regional elevation
+     * data queries. To use this, call createEnvelope() and use that object
+     * to make your elevation queries at a particular LOD. The queries can
+     * be anywhere geographically, but the algorithm will optimize lots of
+     * queries in a particular area (like a terrain tile).
+     *
+     * Each Map contains an ElevationPool you can access for queries against
+     * that Map.
+     *
+     * ElevationEnvelope* envelope = pool->createEnvelope(srs, lod);
+     * float z = envelope->getElevation(point);
+     */
+    class OSGEARTH_EXPORT ElevationPool : public osg::Referenced
+    {
+    public:
+        /** ctor */
+        ElevationPool();
+
+        /** Sets the map from which to read elevation data. */
+        void setMap(const Map* map);
+
+        /**
+         * Sets a vector of ElevationLayers from which to read elevation data.
+         * Omit this is you just want to use all the elevation layes in the Map
+         * that you set with setMap().
+         */
+        void setElevationLayers(const ElevationLayerVector& layers);
+        const ElevationLayerVector& getElevationLayers() const { return _layers; }
+
+        /** Sets the dimension of heightfield tiles to read from the map */
+        void setTileSize(unsigned size);
+        unsigned getTileSize() const { return _tileSize; }
+
+        /** Creates a query envelope to use for elevation queries in a certain area. */
+        ElevationEnvelope* createEnvelope(const SpatialReference* srs, unsigned lod);
+
+        //! Queries the elevation at a GeoPoint for a given LOD.
+        Future<ElevationSample> getElevation(const GeoPoint& p, unsigned lod=23);
+
+        /** Maximum number of elevation tiles to cache */
+        void setMaxEntries(unsigned maxEntries) { _maxEntries = maxEntries; }
+        unsigned getMaxEntries() const          { return _maxEntries; }
+
+        /** Clears any cached tiles from the elevation pool. */
+        void clear();
+        
+        void stopThreading();
+
+    protected:
+
+        osg::observer_ptr<const osg::Referenced> _map;
+
+        ElevationLayerVector _layers;
+
+        enum Status
+        {
+            STATUS_EMPTY = 0u,
+            STATUS_IN_PROGRESS = 1u,
+            STATUS_AVAILABLE = 2u,
+            STATUS_FAIL = 3u
+        };
+
+        // Single elevation tile along with its load status
+        class Tile : public osg::Referenced
+        {
+        public:
+            Tile() : _status(STATUS_EMPTY) { }
+            TileKey             _key;           // key used to request this tile
+            Bounds              _bounds;
+            GeoHeightField      _hf;
+            OpenThreads::Atomic _status;
+            osg::Timer_t        _loadTime;
+        };
+
+        // Custom comparator for Tile that sorts Tiles in a set from
+        // highest resolution to lowest resolution; This ensures we are
+        // sampling overlapping tiles in the proper order
+        struct TileSortHiResToLoRes
+        {
+            bool operator()(
+                const osg::ref_ptr<Tile>& lhs,
+                const osg::ref_ptr<Tile>& rhs) const
+            {
+                // works because the KEY less-than function checks the LOD number
+                // first, which corresponds to the resolution. Higher LOD = higher res.
+                return rhs->_key < lhs->_key;
+            }
+        };
+                
+        // Keeps track of the most-recently-used tiles, in order from MRU (front)
+        // to LRU (back). Multiple pointers to the same Tile may exist in the MRU list.
+        // Once all pointers to a Tile disappear from the MRU (by popping off the back)
+        // the corresponding Tile observer in the Tiles data structure will go NULL
+        // and the Tile will destruct.
+        typedef std::list<osg::ref_ptr<Tile> > MRU;
+        MRU _mru;
+
+        // Cached set of tiles, sorted by TileKey. These are observer pointers; the 
+        // actual references are held in the MRU. That way when all pointers drop off
+        // the back of the MRU, the Tile is destroyed and the main observer goes to 
+        // NULL and is removed.
+        typedef std::map<TileKey, osg::observer_ptr<Tile> > Tiles;
+        Tiles _tiles;
+        Threading::Mutex  _tilesMutex;
+
+        // Track the number of entries in the MRU manually since std::list::size
+        // can be O(n) on some platforms
+        unsigned _entries;
+        unsigned _maxEntries;
+
+        // dimension of sampling heightfield
+        unsigned _tileSize;
+
+        // QuerySet is a collection of Tiles, sorted from high to low resolution,
+        // that a ElevationEnvelope uses for a terrain sampling opteration.
+        typedef std::set<osg::ref_ptr<Tile>, TileSortHiResToLoRes> QuerySet;
+
+        // Asynchronous elevation query operation
+        struct GetElevationOp : public osg::Operation {
+            GetElevationOp(ElevationPool*, const GeoPoint&, unsigned lod);
+            osg::observer_ptr<ElevationPool> _pool;
+            GeoPoint _point;
+            unsigned _lod;
+            Promise<ElevationSample> _promise;
+            void operator()(osg::Object*);
+        };
+        friend struct GetElevationOp;
+        osg::ref_ptr<osg::OperationQueue> _opQueue;
+        std::vector< osg::ref_ptr<osg::OperationThread> > _opThreads;
+
+        virtual ~ElevationPool();
+
+    protected:
+
+        // safely popluate the tile; called when Tile._status = IN_PROGRESS
+        bool fetchTileFromMap(const TileKey& key, MapFrame& frame, Tile* tile);
+        
+        // safely fetch a tile from the central repo, loading from map if necessary
+        bool getTile(const TileKey& key, MapFrame& frame, osg::ref_ptr<Tile>& output);
+        
+        // safely fetch a tile from the central repo, loading from map if necessary
+        bool tryTile(const TileKey& key, MapFrame& frame, osg::ref_ptr<Tile>& output);
+
+        // safely remove the oldest item on the MRU
+        void popMRU();
+
+        // clears and resets the pool.
+        void clearImpl();
+
+        friend class ElevationEnvelope;
+    };
+
+
+    /**
+     * Set of terrain tiles corresponding to a geographic extent that the pager
+     * uses to clamp a feature set. You cannot create this object directly;
+     * instead call ElevationPool::createEnvelope().
+     */
+    class OSGEARTH_EXPORT ElevationEnvelope : public osg::Referenced
+    {
+    public:
+
+        /**
+         * Gets a single elevation, or returns NO_DATA_VALUE upon failure.
+         * The Z value is ignored.
+         */
+        float getElevation(double x, double y);
+
+        /**
+         * Gets a single elevation along with the resolution of the data from
+         * which that sample was taken.
+         */
+        std::pair<float, float> getElevationAndResolution(double x, double y);
+
+        /**
+         * Gets a elevation value for each input point and puts them in output.
+         * Returns the number of successful elevations. Failed queries are set to
+         * NO_DATA_VALUE in the output vector.
+         */
+        unsigned getElevations(
+            const std::vector<osg::Vec3d>& input,
+            std::vector<float>& output);
+
+        /**
+         * Gets the elevation extrema over a collection of point data.
+         * Returns false if the points don't fall inside the envelope
+         */
+        bool getElevationExtrema(
+            const std::vector<osg::Vec3d>& points, 
+            float& out_min, float& out_max);
+
+        /**
+         * The SRS that this envelope expects query points to be in
+         */
+        const SpatialReference* getSRS() const;
+
+        /**
+         * LOD at which this envelope will try to sample the elevation
+         */
+        unsigned getLOD() const { return _lod; }
+
+    protected:
+        ElevationEnvelope();
+        virtual ~ElevationEnvelope();
+
+        ElevationPool::QuerySet _tiles;
+        osg::ref_ptr<const SpatialReference> _inputSRS;
+        unsigned _lod;
+        MapFrame _frame;
+        ElevationPool* _pool;
+        friend class ElevationPool;
+
+    private:
+        bool sample(double x, double y, float& out_elevation, float& out_resolution);
+    };
+
+} // namespace
+
+#endif // OSGEARTH_ELEVATION_POOL_H
diff --git a/src/osgEarth/ElevationPool.cpp b/src/osgEarth/ElevationPool.cpp
new file mode 100644
index 0000000..4606fb7
--- /dev/null
+++ b/src/osgEarth/ElevationPool.cpp
@@ -0,0 +1,513 @@
+
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2008-2016 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <osgEarth/ElevationPool>
+#include <osgEarth/TileKey>
+#include <osgEarth/MapFrame>
+#include <osgEarth/Map>
+#include <osgEarth/Metrics>
+#include <osgEarth/Registry>
+#include <osg/Shape>
+
+using namespace osgEarth;
+
+#define LC "[ElevationPool] "
+
+#define OE_TEST OE_DEBUG
+
+
+ElevationPool::ElevationPool() :
+_entries(0u),
+_maxEntries( 128u ),
+_tileSize( 257u )
+{
+    //nop
+    //_opQueue = Registry::instance()->getAsyncOperationQueue();
+    if (!_opQueue.valid())
+    {
+        _opQueue = new osg::OperationQueue();
+        for (unsigned i=0; i<2; ++i)
+        {
+            osg::OperationThread* thread = new osg::OperationThread();
+            thread->setOperationQueue(_opQueue.get());
+            thread->start();
+            _opThreads.push_back(thread);
+        }
+    }
+}
+
+ElevationPool::~ElevationPool()
+{
+    stopThreading();
+}
+
+void
+ElevationPool::setMap(const Map* map)
+{
+    Threading::ScopedMutexLock lock(_tilesMutex);
+    _map = map;
+    clearImpl();
+}
+
+void
+ElevationPool::clear()
+{
+    Threading::ScopedMutexLock lock(_tilesMutex);
+    clearImpl();
+}
+
+void
+ElevationPool::stopThreading()
+{
+    _opQueue->releaseAllOperations();
+    
+    for (unsigned i = 0; i<_opThreads.size(); ++i)
+    _opThreads[i]->setDone(true);
+}
+
+void
+ElevationPool::setElevationLayers(const ElevationLayerVector& layers)
+{
+    Threading::ScopedMutexLock lock(_tilesMutex);
+    _layers = layers;
+    clearImpl();
+}
+
+void
+ElevationPool::setTileSize(unsigned value)
+{
+    Threading::ScopedMutexLock lock(_tilesMutex);
+    _tileSize = value;
+    clearImpl();
+}
+
+Future<ElevationSample>
+ElevationPool::getElevation(const GeoPoint& point, unsigned lod)
+{
+    GetElevationOp* op = new GetElevationOp(this, point, lod);
+    Future<ElevationSample> result = op->_promise.getFuture();
+    _opQueue->add(op);
+    return result;
+}
+
+ElevationPool::GetElevationOp::GetElevationOp(ElevationPool* pool, const GeoPoint& point, unsigned lod) :
+_pool(pool), _point(point), _lod(lod)
+{
+    //nop
+}
+
+void
+ElevationPool::GetElevationOp::operator()(osg::Object*)
+{
+    osg::ref_ptr<ElevationPool> pool;
+    if (!_promise.isAbandoned() && _pool.lock(pool))
+    {
+        osg::ref_ptr<ElevationEnvelope> env = pool->createEnvelope(_point.getSRS(), _lod);
+        std::pair<float, float> r = env->getElevationAndResolution(_point.x(), _point.y());
+        _promise.resolve(new ElevationSample(r.first, r.second));
+    }
+}
+
+bool
+ElevationPool::fetchTileFromMap(const TileKey& key, MapFrame& frame, Tile* tile)
+{
+    tile->_loadTime = osg::Timer::instance()->tick();
+
+    osg::ref_ptr<osg::HeightField> hf = new osg::HeightField();
+    hf->allocate( _tileSize, _tileSize );
+
+    // Initialize the heightfield to nodata
+    hf->getFloatArray()->assign( hf->getFloatArray()->size(), NO_DATA_VALUE );
+
+    TileKey keyToUse = key;
+    while( !tile->_hf.valid() && keyToUse.valid() )
+    {
+        bool ok;
+        if (_layers.empty())
+        {
+            OE_TEST << LC << "Populating from FULL MAP (" << keyToUse.str() << ")\n";
+            ok = frame.populateHeightField(hf, keyToUse, false /*heightsAsHAE*/, 0L);
+        }
+        else
+        {
+            OE_TEST << LC << "Populating from layers (" << keyToUse.str() << ")\n";
+            ok = _layers.populateHeightFieldAndNormalMap(hf.get(), 0L, keyToUse, 0L, INTERP_BILINEAR, 0L);
+        }
+
+        if (ok)
+        {
+            tile->_hf = GeoHeightField( hf.get(), keyToUse.getExtent() );
+            tile->_bounds = keyToUse.getExtent().bounds();
+        }
+        else
+        {
+            keyToUse = keyToUse.createParentKey();
+        }
+    }
+
+    return tile->_hf.valid();
+}
+
+void
+ElevationPool::popMRU()
+{
+    // first rememeber the key of the item we're about the pop:
+    TileKey key = _mru.back()->_key;
+
+    // establish a temporary observer on the item:
+    osg::observer_ptr<Tile> temp = _mru.back().get();
+
+    // pop the Tile from the MRU:
+    _mru.pop_back();
+
+    // if our observer went to NULL, we know there are no more pointers
+    // to that Tile in the MRU, and we can remove it from the main list:
+    if (!temp.valid())
+    {
+        _tiles.erase(key);
+    }
+}
+
+bool
+ElevationPool::tryTile(const TileKey& key, MapFrame& frame, osg::ref_ptr<Tile>& out)
+{
+    // first see whether the tile is available
+    _tilesMutex.lock();
+
+    // locate the tile in the local tile cache:
+    osg::observer_ptr<Tile>& tile_obs = _tiles[key];
+
+    osg::ref_ptr<Tile> tile;
+
+    // Get a safe pointer to it. If this is NULL, we need to create and
+    // fetch a new tile from the Map.
+    if (!tile_obs.lock(tile))
+    {
+        // a new tile; status -> EMPTY
+        tile = new Tile();
+        tile->_key = key;
+
+        // update the LRU:
+        _mru.push_front(tile.get());
+
+        // prune the MRU if necessary:
+        if (++_entries > _maxEntries )
+        {
+            popMRU();
+            --_entries;
+        }
+
+        // add to the main cache (after putting it on the LRU).
+        tile_obs = tile;
+    }
+       
+    // This means the tile object exists but has yet to be populated:
+    if ( tile->_status == STATUS_EMPTY )
+    {
+        OE_TEST << "  getTile(" << key.str() << ") -> fetch from map\n";
+        tile->_status.exchange(STATUS_IN_PROGRESS);
+        _tilesMutex.unlock();
+
+        bool ok = fetchTileFromMap(key, frame, tile.get());
+        tile->_status.exchange( ok ? STATUS_AVAILABLE : STATUS_FAIL );
+        
+        out = ok ? tile.get() : 0L;
+        return ok;
+    }
+
+    // This means the tile object is populated and available for use:
+    else if ( tile->_status == STATUS_AVAILABLE )
+    {
+        OE_TEST << "  getTile(" << key.str() << ") -> available\n";
+        out = tile.get();
+
+        // Mark this tile as recently used:
+        _mru.push_front(tile.get());
+
+        // prune the MRU if necessary
+        if (++_entries > _maxEntries)
+        {
+            popMRU();
+            --_entries;
+        }
+
+        _tilesMutex.unlock();
+        return true;
+    }
+
+    // This means the attempt to populate the tile with data failed.
+    else if ( tile->_status == STATUS_FAIL )
+    {
+        OE_TEST << "  getTile(" << key.str() << ") -> fail\n";
+        _tilesMutex.unlock();
+        out = 0L;
+        return false;
+    }
+
+    // This means tile data fetch is still in progress (in another thread)
+    // and the caller should check back later.
+    else //if ( tile->_status == STATUS_IN_PROGRESS )
+    {
+        OE_DEBUG << "  getTile(" << key.str() << ") -> in progress...waiting\n";
+        _tilesMutex.unlock();
+        out = 0L;
+        return true;            // out:NULL => check back later please.
+    }
+}
+
+void
+ElevationPool::clearImpl()
+{
+    // assumes the tiles lock is taken.
+    _tiles.clear();
+    _mru.clear();
+    _entries = 0u;
+}
+
+bool
+ElevationPool::getTile(const TileKey& key, MapFrame& frame, osg::ref_ptr<ElevationPool::Tile>& output)
+{
+    // Synchronize the MapFrame to its Map; if there's an update,
+    // clear out the internal cache and MRU.
+    if ( frame.needsSync() )
+    {
+        if (frame.sync())
+        {
+            // Probably unnecessary because the Map itself will clear the pool.
+            clear();
+        }
+    }
+   
+    OE_START_TIMER(get);
+
+    const double timeout = 30.0;
+    osg::ref_ptr<Tile> tile;
+    while( tryTile(key, frame, tile) && !tile.valid() && OE_GET_TIMER(get) < timeout)
+    {
+        // condition: another thread is working on fetching the tile from the map,
+        // so wait and try again later. Do this until we succeed or time out.
+        OpenThreads::Thread::YieldCurrentThread();
+    }
+
+    if ( !tile.valid() && OE_GET_TIMER(get) >= timeout )
+    {
+        // this means we timed out trying to fetch the map tile.
+        OE_TEST << LC << "Timout fetching tile " << key.str() << std::endl;
+    }
+
+    if ( tile.valid() )
+    {
+        if ( tile->_hf.valid() )
+        {
+            // got a valid tile, so push it to the query set.
+            output = tile.get();
+        }
+        else
+        {
+            OE_WARN << LC << "Got a tile with an invalid HF (" << key.str() << ")\n";
+        }
+    }
+
+    return tile.valid();
+}
+
+ElevationEnvelope*
+ElevationPool::createEnvelope(const SpatialReference* srs, unsigned lod)
+{
+    ElevationEnvelope* e = new ElevationEnvelope();
+    e->_inputSRS = srs;
+    osg::ref_ptr<const osg::Referenced> map;
+    if (_map.lock(map))
+        e->_frame.setMap(static_cast<const Map*>(map.get()));
+    e->_lod = lod;
+    e->_pool = this;
+    return e;
+}
+
+//........................................................................
+
+ElevationEnvelope::ElevationEnvelope() :
+_pool(0L)
+{
+    //nop
+}
+
+ElevationEnvelope::~ElevationEnvelope()
+{
+    //nop
+}
+
+bool
+ElevationEnvelope::sample(double x, double y, float& out_elevation, float& out_resolution)
+{
+    out_elevation = NO_DATA_VALUE;
+    out_resolution = 0.0f;
+    bool foundTile = false;
+
+    GeoPoint p(_inputSRS.get(), x, y, 0.0f, ALTMODE_ABSOLUTE);
+
+    if (p.transformInPlace(_frame.getProfile()->getSRS()))
+    {
+        // find the tile containing the point:
+        for(ElevationPool::QuerySet::const_iterator tile_ref = _tiles.begin();
+            tile_ref != _tiles.end();
+            ++tile_ref)
+        {
+            ElevationPool::Tile* tile = tile_ref->get();
+
+            if (tile->_bounds.contains(p.x(), p.y()))
+            {
+                foundTile = true;
+
+                // Found an intersecting tile; sample the elevation:
+                if (tile->_hf.getElevation(0L, p.x(), p.y(), INTERP_BILINEAR, 0L, out_elevation))
+                {
+                    out_resolution = tile->_hf.getXInterval();
+                    // got it; finished
+                    break;
+                }
+            }
+        }
+
+        // If we didn't find a tile containing the point, we need to ask the clamper
+        // for the tile so we can add it to the query set.
+        if (!foundTile)
+        {
+            TileKey key = _frame.getProfile()->createTileKey(p.x(), p.y(), _lod);
+            osg::ref_ptr<ElevationPool::Tile> tile;
+            if (_pool && _pool->getTile(key, _frame, tile))
+            {
+                // Got the new tile; put it in the query set:
+                _tiles.insert(tile.get());
+
+                // Then sample the elevation:
+                if (tile->_hf.getElevation(0L, p.x(), p.y(), INTERP_BILINEAR, 0L, out_elevation))
+                {
+                    out_resolution = 0.5*(tile->_hf.getXInterval() + tile->_hf.getYInterval());
+                }
+            }
+        }
+    }
+    else
+    {
+        OE_WARN << LC << "sample: xform failed" << std::endl;
+    }
+
+    // push the result, even if it was not found and it's NO_DATA_VALUE
+    return out_elevation != NO_DATA_VALUE;
+}
+
+float
+ElevationEnvelope::getElevation(double x, double y)
+{
+    METRIC_SCOPED("ElevationEnvelope::getElevation");
+    float elevation, resolution;
+    sample(x, y, elevation, resolution);
+    return elevation;
+}
+
+std::pair<float, float>
+ElevationEnvelope::getElevationAndResolution(double x, double y)
+{
+    METRIC_SCOPED("ElevationEnvelope::getElevationAndResolution");
+    float elevation, resolution;
+    sample(x, y, elevation, resolution);
+    return std::make_pair(elevation, resolution);
+}
+
+unsigned
+ElevationEnvelope::getElevations(const std::vector<osg::Vec3d>& input,
+                                 std::vector<float>& output)
+{
+    METRIC_SCOPED_EX("ElevationEnvelope::getElevations", 1, "num", toString(input.size()).c_str());
+
+    unsigned count = 0u;
+
+    output.reserve(input.size());
+    output.clear();
+
+    // for each input point:
+    for (std::vector<osg::Vec3d>::const_iterator v = input.begin(); v != input.end(); ++v)
+    {
+        float elevation, resolution;
+        sample(v->x(), v->y(), elevation, resolution);
+        output.push_back(elevation);
+        if (elevation != NO_DATA_VALUE)
+            ++count;
+    }
+
+    if (count < input.size())
+    {
+        OE_WARN << LC << "Issue: Envelope had failed samples" << std::endl;
+        for (ElevationPool::QuerySet::const_iterator tile_ref = _tiles.begin(); tile_ref != _tiles.end(); ++tile_ref)
+        {
+            ElevationPool::Tile* tile = tile_ref->get();
+            OE_WARN << LC << " ... tile " << tile->_bounds.toString() << std::endl;
+        }
+        OE_WARN << LC << std::endl;
+    }
+
+    return count;
+}
+
+bool
+ElevationEnvelope::getElevationExtrema(const std::vector<osg::Vec3d>& input,
+                                       float& min, float& max)
+{
+    if (input.empty())
+        return false;
+
+    min = FLT_MAX, max = -FLT_MAX;
+
+    osg::Vec3d centroid;
+
+    for (std::vector<osg::Vec3d>::const_iterator v = input.begin(); v != input.end(); ++v)
+    {
+        centroid += *v;
+
+        float elevation, resolution;
+        
+        if (sample(v->x(), v->y(), elevation, resolution))
+        {
+            if (elevation < min) min = elevation;
+            if (elevation > max) max = elevation;
+        }
+    }
+
+    // If none of the feature points clamped, try the feature centroid.
+    // It's possible (but improbable) that the feature encloses the envelope
+    if (min > max)
+    {
+        centroid /= input.size();
+
+        float elevation, resolution;
+        if (sample(centroid.x(), centroid.y(), elevation, resolution))
+        {
+            if (elevation < min) min = elevation;
+            if (elevation > max) max = elevation;
+        }
+    }
+
+    return (min <= max);
+}
+
+const SpatialReference*
+ElevationEnvelope::getSRS() const
+{
+    return _inputSRS.get();
+}
diff --git a/src/osgEarth/ElevationQuery b/src/osgEarth/ElevationQuery
index b3ab47f..f1f3831 100644
--- a/src/osgEarth/ElevationQuery
+++ b/src/osgEarth/ElevationQuery
@@ -20,43 +20,21 @@
 #define OSGEARTH_ELEVATION_QUERY_H 1
 
 #include <osgEarth/MapFrame>
+#include <osgEarth/ElevationPool>
 #include <osgEarth/Containers>
-#include <osgEarth/DPLineSegmentIntersector>
+#include <osgEarth/ModelLayer>
+#include <osgUtil/LineSegmentIntersector>
 
 namespace osgEarth
 {
-    class OSGEARTH_EXPORT ElevationQueryCacheReadCallback : public osgUtil::IntersectionVisitor::ReadCallback
-    {
-        public:
-            ElevationQueryCacheReadCallback();
-
-            void setMaximumNumOfFilesToCache(unsigned int maxNumFilesToCache) { _maxNumFilesToCache = maxNumFilesToCache; }
-            unsigned int  getMaximumNumOfFilesToCache() const { return _maxNumFilesToCache; }
-
-            void clearDatabaseCache();
-
-            void pruneUnusedDatabaseCache();
-
-#if OSG_VERSION_GREATER_OR_EQUAL(3,5,0)
-            virtual osg::ref_ptr<osg::Node> readNodeFile(const std::string& filename);
-#else
-            virtual osg::Node* readNodeFile(const std::string& filename);
-#endif
-
-        protected:
-
-            typedef std::map<std::string, osg::ref_ptr<osg::Node> > FileNameSceneMap;
-
-            unsigned int _maxNumFilesToCache;
-            OpenThreads::Mutex  _mutex;
-            FileNameSceneMap    _filenameSceneMap;
-    };
-
-
     /**
+     * @deprecated - Use ElevationPool instead, via Map::getElevationPool to
+     *    query elevation data, and use Terrain::getHeight to query the in-scene
+     *    terrain graph.
+     *
      * ElevationQuery (EQ) lets you query the elevation at any point on a map.
      *
-     * Rather than intersecting with a loaded scene graph, EQ uses the osgEarth
+     * Rather than intersecting with a loadeEd scene graph, EQ uses the osgEarth
      * engine to directly access the best terrain tile for elevation query. You
      * give it the DEM resolution at which you want an elevation point, and it will
      * access the necessary tile and sample it.
@@ -89,30 +67,31 @@ namespace osgEarth
          */
         ElevationQuery( const MapFrame& mapFrame );
 
-        /** dtor */
-        virtual ~ElevationQuery() { }
+        /**
+         * Sets a new map
+         */
+        void setMap(const Map* map);
 
-        /** Sets a new map frame */
+        /**
+         * Sets a new map frame
+         */
         void setMapFrame(const MapFrame& frame);
 
         /**
-         * Gets the terrain elevation at a point, given a terrain resolution.
+         * Gets the elevation data at a point, given a terrain resolution.
          *
          * @param point
          *      Coordinates for which to query elevation.
-         * @param out_elevation
-         *      Stores the elevation result in this variable upon success.
          * @param desiredResolution
          *      Optimal resolution of elevation data to use for the query (if available).
          *      Pass in 0 (zero) to use the best available resolution.
          * @param out_resolution
          *      Resolution of the resulting elevation value (if the method returns true).
          *
-         * @return True if the query succeeded, false upon failure.
+         * @return Sampled elevation value (or NO_DATA_VALUE)
          */
-        bool getElevation(
+        float getElevation(
             const GeoPoint& point,
-            double&         out_elevation,
             double          desiredResolution    =0.0,
             double*         out_actualResolution =0L );
 
@@ -134,85 +113,38 @@ namespace osgEarth
         bool getElevations(
             const std::vector<osg::Vec3d>& points,
             const SpatialReference*        pointsSRS,
-            std::vector<double>&           out_elevations,
-            double                         desiredResolution = 0.0 );
+            std::vector<float>&            out_elevations,
+            double                         desiredResolution =0.0 );
 
-        /**
-         * Whether a query should fall back on lower resolution data if no results
-         * are available at the requested resolution. Default is true.
-         */
-        void setFallBackOnNoData(bool value) { _fallBackOnNoData = value; }
-        bool getFallBackOnNoData() const { return _fallBackOnNoData; }
-
-        /**
-         * Sets the maximum cache size for elevation tiles.
-         */
-        void setMaxTilesToCache( int value );
-
-        /**
-         * Gets the maximum cache size for elevation tiles.
-         */
-        int getMaxTilesToCache() const;
-
-        /**
-        * Sets the maximum level override for elevation queries.
-        * A value of -1 turns off the override.
-        */
-        void setMaxLevelOverride(int maxLevelOverride);
-
-        /**
-        * Gets the maximum level override for elevation queries.
-        */
-        int getMaxLevelOverride() const;
-
-        /**
-         * Gets the average time per query
-         */
-        double getAverageQueryTime() const { return _queries > 0.0 ? _totalTime/_queries : 0.0; }
-
-        /**
-         * Gets the maximum level of data available at the given point.  If the layers have DataExtents provided they
-         * will be queried.  This allows certain areas on the earth to have higher levels of detail
-         * than others and avoids unnecessary queries where there isn't high resolution data available.
-         * Negative return value means there was no data available at the location.
-         */
-        int getMaxLevel(double x, double y, const SpatialReference* srs, const Profile* profile, unsigned tileSize) const;
+        /** dtor */
+        virtual ~ElevationQuery() { }
 
-        /** Clear the database cache.*/
-        void clearDatabaseCache() { if (_eqcrc.valid()) _eqcrc->clearDatabaseCache(); }
+    private:
+        // Map to query
+        MapFrame _mapf;
 
-        /** Set the ReadCallback that does the reading of external PagedLOD models, and caching of loaded subgraphs.
-          * Note, if you have multiple LineOfSight or HeightAboveTerrain objects in use at one time then you should share a single
-          * DatabaseCacheReadCallback between all of them. */
-        void setElevationQueryCacheReadCallback(ElevationQueryCacheReadCallback* eqcrc);
+        // Map model layers marked as terrain patches
+        std::vector<ModelLayer*> _patchLayers;
 
-        /** Get the ReadCallback that does the reading of external PagedLOD models, and caching of loaded subgraphs.*/
-        ElevationQueryCacheReadCallback* getElevationQueryCacheReadCallback() { return _eqcrc.get(); }
+        // Intersector to query the patch layers
+        osg::ref_ptr<osgUtil::LineSegmentIntersector> _patchLayersLSI;
 
-    private:
-        MapFrame     _mapf;
-        int          _maxLevelOverride;
-        bool         _fallBackOnNoData;
-
-        typedef LRUCache< TileKey, GeoHeightField > TileCache;
-        TileCache _cache;
-        double _queries;
-        double _totalTime;
-        std::vector<ModelLayer*> _patchLayers;
-        osg::ref_ptr<DPLineSegmentIntersector> _patchLayersLSI;
+        // Callback to force the intersector to materialize paged model nodes
+        osg::ref_ptr<osgUtil::IntersectionVisitor::ReadCallback> _ivrc;
 
-        osg::ref_ptr<ElevationQueryCacheReadCallback> _eqcrc;
+        // Active elevation sampler
+        osg::ref_ptr<ElevationEnvelope> _envelope;
 
     private:
-        void postCTOR();
+        void reset();
         void sync();
         void gatherPatchLayers();
 
         bool getElevationImpl(
             const GeoPoint& point,
-            double&         out_elevation,
+            float&          out_elevation,
             double          desiredResolution,
-            double*         out_actualResolution =0L );
+            double*         out_actualResolution );
     };
 
 } // namespace osgEarth
diff --git a/src/osgEarth/ElevationQuery.cpp b/src/osgEarth/ElevationQuery.cpp
index 39d8ea6..81a99a9 100644
--- a/src/osgEarth/ElevationQuery.cpp
+++ b/src/osgEarth/ElevationQuery.cpp
@@ -17,145 +17,57 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>
  */
 #include <osgEarth/ElevationQuery>
-#include <osgEarth/Locators>
-#include <osgEarth/HeightFieldUtils>
-#include <osgEarth/DPLineSegmentIntersector>
+#include <osgUtil/LineSegmentIntersector>
+#include <osgEarth/Map>
+#include <osgEarth/ElevationPool>
 #include <osgUtil/IntersectionVisitor>
+#include <osgSim/LineOfSight>
 
 #define LC "[ElevationQuery] "
 
 using namespace osgEarth;
-using namespace OpenThreads;
 
-namespace
-{
-    int nextPowerOf2(int x) {
-        --x;
-        x |= x >> 1;
-        x |= x >> 2;
-        x |= x >> 4;
-        x |= x >> 8;
-        x |= x >> 16;
-        return x+1;
-    }
-}
 
-ElevationQueryCacheReadCallback::ElevationQueryCacheReadCallback()
+ElevationQuery::ElevationQuery()
 {
-    _maxNumFilesToCache = 2000;
+    reset();
 }
 
-void ElevationQueryCacheReadCallback::clearDatabaseCache()
+ElevationQuery::ElevationQuery(const Map* map)
 {
-    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-    _filenameSceneMap.clear();
+    setMap(map);
 }
 
-void ElevationQueryCacheReadCallback::pruneUnusedDatabaseCache()
+ElevationQuery::ElevationQuery(const MapFrame& mapFrame)
 {
+    setMapFrame(mapFrame);
 }
 
-#if OSG_VERSION_GREATER_OR_EQUAL(3,5,0)
-osg::ref_ptr<osg::Node> ElevationQueryCacheReadCallback::readNodeFile(const std::string& filename)
-#else
-osg::Node* ElevationQueryCacheReadCallback::readNodeFile(const std::string& filename)
-#endif
-{
-    // first check to see if file is already loaded.
-    {
-        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-
-        FileNameSceneMap::iterator itr = _filenameSceneMap.find(filename);
-        if (itr != _filenameSceneMap.end())
-        {
-            OSG_INFO<<"Getting from cache "<<filename<<std::endl;
-
-            return itr->second.get();
-        }
-    }
-
-    // now load the file.
-    osg::ref_ptr<osg::Node> node = osgDB::readRefNodeFile(filename);
-
-    // insert into the cache.
-    if (node.valid())
-    {
-        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
-
-        if (_filenameSceneMap.size() < _maxNumFilesToCache)
-        {
-            OSG_INFO<<"Inserting into cache "<<filename<<std::endl;
-
-            _filenameSceneMap[filename] = node;
-        }
-        else
-        {
-            // for time being implement a crude search for a candidate to chuck out from the cache.
-            for(FileNameSceneMap::iterator itr = _filenameSceneMap.begin();
-                itr != _filenameSceneMap.end();
-                ++itr)
-            {
-                if (itr->second->referenceCount()==1)
-                {
-                    OSG_NOTICE<<"Erasing "<<itr->first<<std::endl;
-                    // found a node which is only referenced in the cache so we can discard it
-                    // and know that the actual memory will be released.
-                    _filenameSceneMap.erase(itr);
-                    break;
-                }
-            }
-            OSG_INFO<<"And the replacing with "<<filename<<std::endl;
-            _filenameSceneMap[filename] = node;
-        }
-    }
-
-#if OSG_VERSION_GREATER_OR_EQUAL(3,5,0)
-    return node;
-#else
-    return node.release();
-#endif
-}
-
-ElevationQuery::ElevationQuery()
-{
-    postCTOR();
-}
-
-ElevationQuery::ElevationQuery(const Map* map) :
-_mapf( map, (Map::ModelParts)(Map::TERRAIN_LAYERS | Map::MODEL_LAYERS) )
-{
-    postCTOR();
-}
-
-ElevationQuery::ElevationQuery(const MapFrame& mapFrame) :
-_mapf( mapFrame )
+void
+ElevationQuery::setMap(const Map* map)
 {
-    postCTOR();
+    _mapf.setMap(map);
+    reset();
 }
 
 void
 ElevationQuery::setMapFrame(const MapFrame& frame)
 {
     _mapf = frame;
-    postCTOR();
+    reset();
 }
 
 void
-ElevationQuery::postCTOR()
+ElevationQuery::reset()
 {
-    // defaults:
-    _maxLevelOverride = -1;
-    _queries          = 0.0;
-    _totalTime        = 0.0;
-    _fallBackOnNoData = false;
-    _cache.clear();
-    _cache.setMaxSize( 500 );
-
     // set read callback for IntersectionVisitor
-    setElevationQueryCacheReadCallback(new ElevationQueryCacheReadCallback);
+    _ivrc = new osgSim::DatabaseCacheReadCallback();
 
     // find terrain patch layers.
     gatherPatchLayers();
+
+    // clear any active envelope
+    _envelope = 0L;
 }
 
 void
@@ -164,8 +76,7 @@ ElevationQuery::sync()
     if ( _mapf.needsSync() )
     {
         _mapf.sync();
-        _cache.clear();
-        gatherPatchLayers();
+        reset();
     }
 }
 
@@ -174,8 +85,10 @@ ElevationQuery::gatherPatchLayers()
 {
     // cache a vector of terrain patch models.
     _patchLayers.clear();
-    for(ModelLayerVector::const_iterator i = _mapf.modelLayers().begin();
-        i != _mapf.modelLayers().end();
+    ModelLayerVector modelLayers;
+    _mapf.getLayers(modelLayers);
+    for(ModelLayerVector::const_iterator i = modelLayers.begin();
+        i != modelLayers.end();
         ++i)
     {
         if ( i->get()->isTerrainPatch() )
@@ -183,146 +96,27 @@ ElevationQuery::gatherPatchLayers()
     }
 }
 
-int
-ElevationQuery::getMaxLevel( double x, double y, const SpatialReference* srs, const Profile* profile, unsigned tileSize) const
-{
-    int targetTileSizePOT = nextPowerOf2((int)tileSize);
-
-    int maxLevel = -1;
-
-    for( ElevationLayerVector::const_iterator i = _mapf.elevationLayers().begin(); i != _mapf.elevationLayers().end(); ++i )
-    {
-        const ElevationLayer* layer = i->get();
-
-        // skip disabled layers
-        if ( !layer->getEnabled() || !layer->getVisible() )
-            continue;
-
-        optional<int> layerMaxLevel;
-
-        osgEarth::TileSource* ts = layer->getTileSource();
-        if ( ts )
-        {
-            // TileSource is good; check for optional data extents:
-            if ( ts->getDataExtents().size() > 0 )
-            {
-                osg::Vec3d tsCoord(x, y, 0);
-
-                const SpatialReference* tsSRS = ts->getProfile() ? ts->getProfile()->getSRS() : 0L;
-                if ( srs && tsSRS )
-                    srs->transform(tsCoord, tsSRS, tsCoord);
-                else
-                    tsSRS = srs;
-
-                for (osgEarth::DataExtentList::iterator j = ts->getDataExtents().begin(); j != ts->getDataExtents().end(); j++)
-                {
-                    if (j->maxLevel().isSet() &&
-                        (!layerMaxLevel.isSet() || j->maxLevel() > layerMaxLevel.get() )
-                        && j->contains( tsCoord.x(), tsCoord.y(), tsSRS ))
-                    {
-                        layerMaxLevel = j->maxLevel().value();
-                    }
-                }
-            }
-            else
-            {
-                // Just use the default max level.  Without any data extents we don't know the actual max
-                layerMaxLevel = (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel());
-            }
-
-            // cap the max to the layer's express max level (if set).
-            if ( layerMaxLevel.isSet() && layer->getTerrainLayerRuntimeOptions().maxLevel().isSet() )
-            {
-                layerMaxLevel = std::min( layerMaxLevel.get(), (int)(*layer->getTerrainLayerRuntimeOptions().maxLevel()) );
-            }
-
-            // Need to convert the layer max of this TileSource to that of the actual profile
-            if ( layerMaxLevel.isSet() )
-            {
-                layerMaxLevel = profile->getEquivalentLOD( ts->getProfile(), layerMaxLevel.get() );
-            }
-        }
-        else
-        {
-            // no TileSource? probably in cache-only mode. Use the layer max (or its default).
-            layerMaxLevel = (int)(layer->getTerrainLayerRuntimeOptions().maxLevel().value());
-        }
 
-        // Adjust for the tile size resolution differential, if supported by the layer.
-        if ( layerMaxLevel.isSet() )
-        {
-			// TODO:  This native max resolution of the layer has already been computed here.
-			//        The following block attempts to compute a higher resolution to undo the resolution
-			//        mapping that populateHeightField will eventually do.  So for example, you might compute a maximum level of
-			//        10 here, and this will adjust it to 14 with the knowledge that populateHeightField will adjust the 14 back to 10.
-			//        The use of populateHeightField needs to be replaced by code that just works with the native resolution of the
-			//        layers instead.
-#if 1
-            int layerTileSize = layer->getTileSize();
-            if (layerTileSize > targetTileSizePOT)
-            {
-                int temp = std::max(targetTileSizePOT, 2);
-                while(temp < layerTileSize)
-                {
-                    temp *= 2;
-                    layerMaxLevel = layerMaxLevel.get() + 1;
-                }
-            }
-#endif
-
-            if (layerMaxLevel > maxLevel)
-            {
-                maxLevel = layerMaxLevel.get();
-            }
-        }
-    }
-
-    return maxLevel;
-}
-
-
-void
-ElevationQuery::setMaxTilesToCache( int value )
-{
-    _cache.setMaxSize( value );
-}
-
-int
-ElevationQuery::getMaxTilesToCache() const
-{
-    return _cache.getMaxSize();
-}
-
-void
-ElevationQuery::setMaxLevelOverride(int maxLevelOverride)
-{
-    _maxLevelOverride = maxLevelOverride;
-}
-
-int
-ElevationQuery::getMaxLevelOverride() const
+float
+ElevationQuery::getElevation(const GeoPoint& point,
+                             double          desiredResolution,
+                             double*         out_actualResolution)
 {
-    return _maxLevelOverride;
-}
+    float result = NO_DATA_VALUE;
 
-bool
-ElevationQuery::getElevation(const GeoPoint&         point,
-                             double&                 out_elevation,
-                             double                  desiredResolution,
-                             double*                 out_actualResolution)
-{
     sync();
     if ( point.altitudeMode() == ALTMODE_ABSOLUTE )
     {
-        return getElevationImpl( point, out_elevation, desiredResolution, out_actualResolution );
+        getElevationImpl( point, result, desiredResolution, out_actualResolution );
     }
     else
     {
         GeoPoint point_abs( point.getSRS(), point.x(), point.y(), 0.0, ALTMODE_ABSOLUTE );
-        return getElevationImpl( point_abs, out_elevation, desiredResolution, out_actualResolution );
+        getElevationImpl( point_abs, result, desiredResolution, out_actualResolution );
     }
-}
 
+    return result;
+}
 
 bool
 ElevationQuery::getElevations(std::vector<osg::Vec3d>& points,
@@ -333,11 +127,16 @@ ElevationQuery::getElevations(std::vector<osg::Vec3d>& points,
     sync();
     for( osg::Vec3dArray::iterator i = points.begin(); i != points.end(); ++i )
     {
-        double elevation;
+        float elevation;
         double z = (*i).z();
         GeoPoint p(pointsSRS, *i, ALTMODE_ABSOLUTE);
-        if ( getElevationImpl( p, elevation, desiredResolution ) )
+        if ( getElevationImpl(p, elevation, desiredResolution, 0L))
         {
+            if (elevation == NO_DATA_VALUE)
+            {
+                elevation = 0.0;
+            }
+
             (*i).z() = ignoreZ ? elevation : elevation + z;
         }
     }
@@ -347,16 +146,16 @@ ElevationQuery::getElevations(std::vector<osg::Vec3d>& points,
 bool
 ElevationQuery::getElevations(const std::vector<osg::Vec3d>& points,
                               const SpatialReference*        pointsSRS,
-                              std::vector<double>&           out_elevations,
+                              std::vector<float>&            out_elevations,
                               double                         desiredResolution )
 {
     sync();
     for( osg::Vec3dArray::const_iterator i = points.begin(); i != points.end(); ++i )
     {
-        double elevation;
+        float elevation;
         GeoPoint p(pointsSRS, *i, ALTMODE_ABSOLUTE);
 
-        if ( getElevationImpl(p, elevation, desiredResolution) )
+        if ( getElevationImpl(p, elevation, desiredResolution, 0L) )
         {
             out_elevations.push_back( elevation );
         }
@@ -369,8 +168,8 @@ ElevationQuery::getElevations(const std::vector<osg::Vec3d>& points,
 }
 
 bool
-ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
-                                 double&         out_elevation,
+ElevationQuery::getElevationImpl(const GeoPoint& point,
+                                 float&          out_elevation,
                                  double          desiredResolution,
                                  double*         out_actualResolution)
 {
@@ -388,8 +187,8 @@ ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
     {
         osgUtil::IntersectionVisitor iv;
 
-        if ( _eqcrc.valid() )
-            iv.setReadCallback(_eqcrc);
+        if ( _ivrc.valid() )
+            iv.setReadCallback(_ivrc.get());
 
         for(std::vector<ModelLayer*>::iterator i = _patchLayers.begin(); i != _patchLayers.end(); ++i)
         {
@@ -413,7 +212,7 @@ ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
                     // first time through, set up the intersector on demand
                     if ( !_patchLayersLSI.valid() )
                     {
-                        _patchLayersLSI = new DPLineSegmentIntersector(start, end);
+                        _patchLayersLSI = new osgUtil::LineSegmentIntersector(start, end);
                         _patchLayersLSI->setIntersectionLimit( _patchLayersLSI->LIMIT_NEAREST );
                     }
                     else
@@ -435,7 +234,7 @@ ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
                         // transform back to input SRS:
                         GeoPoint output;
                         output.fromWorld( point.getSRS(), isect );
-                        out_elevation = output.z();
+                        out_elevation = (float)output.z();
                         if ( out_actualResolution )
                             *out_actualResolution = 0.0;
 
@@ -453,118 +252,43 @@ ElevationQuery::getElevationImpl(const GeoPoint& point, /* abs */
     if ( _mapf.elevationLayers().empty() )
     {
         // this means there are no heightfields.
-        out_elevation = 0.0;
+        out_elevation = NO_DATA_VALUE;
         return true;
     }
 
     // tile size (resolution of elevation tiles)
-    unsigned tileSize = 33; // ???
+    unsigned tileSize = 257; // yes?
 
-    // This is the max resolution that we actually have data at this point
-    int bestAvailLevel = getMaxLevel( point.x(), point.y(), point.getSRS(), _mapf.getProfile(), tileSize );
-
-    // A negative value means that no data is avaialble at that point at any resolution.
-    if ( bestAvailLevel < 0 )
-    {
-        return false;
-    }
+    // default LOD:
+    unsigned lod = 23u;
 
+    // attempt to map the requested resolution to an LOD:
     if (desiredResolution > 0.0)
     {
-        int desiredLevel = _mapf.getProfile()->getLevelOfDetailForHorizResolution( desiredResolution, tileSize );
-        if (desiredLevel < bestAvailLevel)
-        {
-            bestAvailLevel = desiredLevel;
-        }
+        int level = _mapf.getProfile()->getLevelOfDetailForHorizResolution(desiredResolution, tileSize);
+        if ( level > 0 )
+            lod = level;
     }
 
-    //OE_NOTICE << LC << "Best available data level " << point.x() << ", " << point.y() << " = "  << bestAvailLevel << std::endl;
-
-    // transform the input coords to map coords:
-    GeoPoint mapPoint = point;
-    if ( point.isValid() && !point.getSRS()->isHorizEquivalentTo( _mapf.getProfile()->getSRS() ) )
-    {
-        mapPoint = point.transform(_mapf.getProfile()->getSRS());
-        if ( !mapPoint.isValid() )
-        {
-            OE_WARN << LC << "Fail: coord transform failed" << std::endl;
-            return false;
-        }
+    // do we need a new ElevationEnvelope?
+    if (!_envelope.valid() ||
+        !point.getSRS()->isHorizEquivalentTo(_envelope->getSRS()) ||
+        lod != _envelope->getLOD())
+    {        
+        _envelope = _mapf.getElevationPool()->createEnvelope(point.getSRS(), lod);
     }
 
-    // get the tilekey corresponding to the tile we need:
-    TileKey key = _mapf.getProfile()->createTileKey( mapPoint.x(), mapPoint.y(), bestAvailLevel );
-    if ( !key.valid() )
+    // sample the elevation, and if requested, the resolution as well:
+    if (out_actualResolution)
     {
-        OE_WARN << LC << "Fail: coords fall outside map" << std::endl;
-        return false;
+        std::pair<float, float> result = _envelope->getElevationAndResolution(point.x(), point.y());
+        out_elevation = result.first;
+        *out_actualResolution = result.second;
     }
-
-    bool result = false;
-
-    while ( !result && key.valid() )
+    else
     {
-        GeoHeightField geoHF;
-
-        // Try to get the hf from the cache
-        TileCache::Record record;
-        if ( _cache.get( key, record ) )
-        {
-            geoHF = record.value();
-        }
-        else
-        {
-            // Create it
-            osg::ref_ptr<osg::HeightField> hf = new osg::HeightField();
-            hf->allocate( tileSize, tileSize );
-
-            // Initialize the heightfield to nodata
-            hf->getFloatArray()->assign( hf->getFloatArray()->size(), NO_DATA_VALUE );
-
-            if (_mapf.populateHeightField(hf, key, false /*heightsAsHAE*/, 0L))
-            {
-                geoHF = GeoHeightField( hf.get(), key.getExtent() );
-                _cache.insert( key, geoHF );
-            }
-        }
-
-        if (geoHF.valid())
-        {
-            float elevation = 0.0f;
-            result = geoHF.getElevation( mapPoint.getSRS(), mapPoint.x(), mapPoint.y(), _mapf.getMapInfo().getElevationInterpolation(), mapPoint.getSRS(), elevation);
-            if (result && elevation != NO_DATA_VALUE)
-            {
-                out_elevation = (double)elevation;
-
-                // report the actual resolution of the heightfield.
-                if ( out_actualResolution )
-                    *out_actualResolution = geoHF.getXInterval();
-            }
-            else
-            {
-                result = false;
-            }
-        }
-
-        if ( geoHF.valid() && !_fallBackOnNoData )
-        {
-            break;
-        }
-        else if ( result == false )
-        {
-            key = key.createParentKey();
-        }
+        out_elevation = _envelope->getElevation(point.x(), point.y());
     }
 
-
-    osg::Timer_t end = osg::Timer::instance()->tick();
-    _queries++;
-    _totalTime += osg::Timer::instance()->delta_s( begin, end );
-
-    return result;
-}
-
-void ElevationQuery::setElevationQueryCacheReadCallback(ElevationQueryCacheReadCallback* eqcrc)
-{
-    _eqcrc = eqcrc;
+    return out_elevation != NO_DATA_VALUE;
 }
diff --git a/src/osgEarth/Endian b/src/osgEarth/Endian
new file mode 100644
index 0000000..cae9478
--- /dev/null
+++ b/src/osgEarth/Endian
@@ -0,0 +1,178 @@
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2016 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+// "License": Public Domain
+// I, Mathias Panzenb�ck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
+// In case there are jurisdictions that don't support putting things in the public domain you can also consider it to
+// be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it
+// an example on how to get the endian conversion functions on different platforms.
+
+#ifndef OSGEARTH_PORTABLE_ENDIAN_H__
+#define OSGEARTH_PORTABLE_ENDIAN_H__
+
+#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
+
+#   include <stdint.h>
+
+#	define __WINDOWS__
+
+#endif
+
+#if defined(__linux__) || defined(__CYGWIN__)
+
+#	include <endian.h>
+
+#elif defined(__APPLE__)
+
+#	include <libkern/OSByteOrder.h>
+
+#	define htobe16(x) OSSwapHostToBigInt16(x)
+#	define htole16(x) OSSwapHostToLittleInt16(x)
+#	define be16toh(x) OSSwapBigToHostInt16(x)
+#	define le16toh(x) OSSwapLittleToHostInt16(x)
+ 
+#	define htobe32(x) OSSwapHostToBigInt32(x)
+#	define htole32(x) OSSwapHostToLittleInt32(x)
+#	define be32toh(x) OSSwapBigToHostInt32(x)
+#	define le32toh(x) OSSwapLittleToHostInt32(x)
+ 
+#	define htobe64(x) OSSwapHostToBigInt64(x)
+#	define htole64(x) OSSwapHostToLittleInt64(x)
+#	define be64toh(x) OSSwapBigToHostInt64(x)
+#	define le64toh(x) OSSwapLittleToHostInt64(x)
+
+#	define __BYTE_ORDER    BYTE_ORDER
+#	define __BIG_ENDIAN    BIG_ENDIAN
+#	define __LITTLE_ENDIAN LITTLE_ENDIAN
+#	define __PDP_ENDIAN    PDP_ENDIAN
+
+#elif defined(__OpenBSD__)
+
+#	include <sys/endian.h>
+
+#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+
+#	include <sys/endian.h>
+
+#	define be16toh(x) betoh16(x)
+#	define le16toh(x) letoh16(x)
+
+#	define be32toh(x) betoh32(x)
+#	define le32toh(x) letoh32(x)
+
+#	define be64toh(x) betoh64(x)
+#	define le64toh(x) letoh64(x)
+
+#elif defined(__WINDOWS__)
+
+#	include <windows.h>
+
+#	if BYTE_ORDER == LITTLE_ENDIAN
+
+#		define htobe16(x) _byteswap_ushort(x)
+#		define htole16(x) (x)
+#		define be16toh(x) _byteswap_ushort(x)
+#		define le16toh(x) (x)
+ 
+#		define htobe32(x) _byteswap_ulong(x)
+#		define htole32(x) (x)
+#		define be32toh(x) _byteswap_ulong(x)
+#		define le32toh(x) (x)
+
+#		if defined(htonll) && defined(ntohll)
+#			define htobe64(x) htonll(x)
+#			define htole64(x) (x)
+#			define be64toh(x) ntohll(x)
+#			define le64toh(x) (x)
+#		else
+#			define htobe64(x) _byteswap_uint64(x)
+#			define htole64(x) (x)
+#			define be64toh(x) _byteswap_uint64(x)
+#			define le64toh(x) (x)
+#		endif
+
+#	elif BYTE_ORDER == BIG_ENDIAN
+
+		/* that would be xbox 360 */
+#		define htobe16(x) (x)
+#		define htole16(x) __builtin_bswap16(x)
+#		define be16toh(x) (x)
+#		define le16toh(x) __builtin_bswap16(x)
+ 
+#		define htobe32(x) (x)
+#		define htole32(x) __builtin_bswap32(x)
+#		define be32toh(x) (x)
+#		define le32toh(x) __builtin_bswap32(x)
+ 
+#		define htobe64(x) (x)
+#		define htole64(x) __builtin_bswap64(x)
+#		define be64toh(x) (x)
+#		define le64toh(x) __builtin_bswap64(x)
+
+#	else
+
+#		error byte order not supported
+
+#	endif
+
+#	define __BYTE_ORDER    BYTE_ORDER
+#	define __BIG_ENDIAN    BIG_ENDIAN
+#	define __LITTLE_ENDIAN LITTLE_ENDIAN
+#	define __PDP_ENDIAN    PDP_ENDIAN
+
+#else
+
+//#	error platform not supported
+#   define htobe16(X) X
+#   define htobe32(X) X
+#   define htobe64(X) X
+#   define be16toh(X) X
+#   define be32toh(X) X
+#   define be64toh(X) X
+
+#endif
+
+#define OE_ENCODE_SHORT(X)  htobe16(X)
+#define OE_ENCODE_INT(X)    htobe32(X)
+#define OE_ENCODE_LONG(X)   htobe32(X)
+
+inline uint32_t OE_ENCODE_FLOAT(float x) {
+    return htobe32(*(uint32_t*)(&x));
+}
+inline uint64_t OE_ENCODE_DOUBLE(double x) {
+    return htobe64(*(uint64_t*)(&x));
+}
+
+#define OE_DECODE_SHORT(X)  be16toh(X)
+#define OE_DECODE_INT(X)    be32toh(X)
+#define OE_DECODE_LONG(X)   be32toh(X)
+
+inline float OE_DECODE_FLOAT(uint32_t x) {
+    uint32_t temp = be32toh(x);
+    float r = *(float*)(&temp);
+    return r;
+}
+inline double OE_DECODE_DOUBLE(uint64_t x) {
+    uint64_t temp = be64toh(x);
+    double r = *(double*)(&temp);
+    return r;
+}
+
+
+#endif // OSGEARTH_PORTABLE_ENDIAN_H__
diff --git a/src/osgEarth/Export b/src/osgEarth/Export
index c793f99..3497808 100644
--- a/src/osgEarth/Export
+++ b/src/osgEarth/Export
@@ -52,6 +52,9 @@
     #  define OSGEARTH_EXPORT
 #endif  
 
+// package-portected indicator:
+#define OSGEARTH_INTERNAL
+
 // set up define for whether member templates are supported by VisualStudio compilers.
 #ifdef _MSC_VER
 # if (_MSC_VER >= 1300)
diff --git a/src/osgEarth/Extension b/src/osgEarth/Extension
index f4acc13..c3b7321 100644
--- a/src/osgEarth/Extension
+++ b/src/osgEarth/Extension
@@ -22,6 +22,7 @@
 
 #include <osgEarth/Common>
 #include <osgEarth/Config>
+#include <osgEarth/PluginLoader>
 #include <osg/Object>
 #include <osgDB/Options>
 #include <osgDB/ReaderWriter>
@@ -134,62 +135,10 @@ namespace osgEarth
     CLASS (const CLASS & rhs, const osg::CopyOp& op) { } \
     virtual ~ CLASS() { }
 
-    
-    template<class T>
-    class RegisterExtensionLoader
-    {
-        public:
-            RegisterExtensionLoader(const std::string& name)
-            {
-                if (osgDB::Registry::instance())
-                {
-                    _rw = new T(name);
-                    osgDB::Registry::instance()->addReaderWriter(_rw.get());
-                }
-            }
-
-            ~RegisterExtensionLoader()
-            {
-                if (osgDB::Registry::instance())
-                {
-                    osgDB::Registry::instance()->removeReaderWriter(_rw.get());
-                }
-            }
-
-            T* get() { return _rw.get(); }
-
-        protected:
-            osg::ref_ptr<T> _rw;
-    };
-
-    /**
-     * This template declares and registers an extension-loading plugin.
-     */
-    template<typename T>
-    class ExtensionLoader : public osgDB::ReaderWriter
-    {
-    public: // Plugin stuff
-        ExtensionLoader(const std::string& name) {
-            supportsExtension( name, name );
-        }
-
-        virtual ~ExtensionLoader() { }
-
-        ReadResult readObject(const std::string& filename, const osgDB::Options* dbOptions) const
-        {
-          if ( !acceptsExtension(osgDB::getLowerCaseFileExtension(filename)) )
-                return ReadResult::FILE_NOT_HANDLED;
-
-          return ReadResult( new T(Extension::getConfigOptions(dbOptions)) );
-        }
-    };
-
 } // namespace osgEarth
 
-
 #define REGISTER_OSGEARTH_EXTENSION(NAME,CLASS) \
     extern "C" void osgdb_##NAME(void) {} \
-    static osgEarth::RegisterExtensionLoader< osgEarth::ExtensionLoader<CLASS> > g_proxy_##CLASS_##NAME( #NAME );
-
+    static osgEarth::RegisterPluginLoader< osgEarth::PluginLoader<CLASS, osgEarth::Extension> > g_proxy_##CLASS_##NAME( #NAME );
 
 #endif
diff --git a/src/osgEarth/FadeEffect b/src/osgEarth/FadeEffect
index ec558f9..33b38a9 100644
--- a/src/osgEarth/FadeEffect
+++ b/src/osgEarth/FadeEffect
@@ -46,7 +46,7 @@ namespace osgEarth
         const optional<float>& maxRange() const { return _maxRange; }
 
         /** Distance over which to fade out geometry (from max range) */
-        optional<float>& attentuationDistance() { return _attenDist; }
+        optional<float>& attenuationDistance() { return _attenDist; }
         const optional<float>& attenuationDistance() const { return _attenDist; }
 
     public:
diff --git a/src/osgEarth/FadeEffect.cpp b/src/osgEarth/FadeEffect.cpp
index a32d0fb..f987e0f 100644
--- a/src/osgEarth/FadeEffect.cpp
+++ b/src/osgEarth/FadeEffect.cpp
@@ -60,7 +60,7 @@ namespace
         "uniform float oe_fadeeffect_attenDist; \n"
         "uniform float osg_FrameTime; \n"
 
-        "varying float oe_fadeeffect_opacity; \n"
+        "out float oe_fadeeffect_opacity; \n"
 
         "void oe_vertFadeEffect(inout vec4 VertexView) \n"
         "{ \n"
@@ -73,7 +73,7 @@ namespace
         "#version " GLSL_VERSION_STR "\n"
         GLSL_DEFAULT_PRECISION_FLOAT "\n"
 
-        "varying float oe_fadeeffect_opacity; \n"
+        "in float oe_fadeeffect_opacity; \n"
 
         "void oe_fragFadeEffect( inout vec4 color ) \n"
         "{ \n"
@@ -165,7 +165,7 @@ namespace
         "#version " GLSL_VERSION_STR "\n"
         GLSL_DEFAULT_PRECISION_FLOAT "\n"
 
-        "varying float oe_FadeLOD_opacity; \n"
+        "in float oe_FadeLOD_opacity; \n"
         "void oe_fragFadeLOD( inout vec4 color ) \n"
         "{ \n"
         "    color.a *= oe_FadeLOD_opacity; \n"
diff --git a/src/osgEarth/FileUtils.cpp b/src/osgEarth/FileUtils.cpp
index f556c83..3ff4902 100644
--- a/src/osgEarth/FileUtils.cpp
+++ b/src/osgEarth/FileUtils.cpp
@@ -20,6 +20,7 @@
 #include <osgEarth/FileUtils>
 #include <osgEarth/StringUtils>
 #include <osgEarth/DateTime>
+#include <osgEarth/ThreadingUtils>
 #include <osgDB/FileUtils>
 #include <osgDB/FileNameUtils>
 #include <osgDB/Registry>
@@ -158,63 +159,99 @@ bool osgEarth::isRelativePath(const std::string& fileName)
 
 std::string osgEarth::getFullPath(const std::string& relativeTo, const std::string &relativePath)
 {
+    // A cache, since this method uses osgDB::getRealPath which can be quite slow.
+    static Threading::Mutex s_cacheMutex;
+    typedef std::map<std::string, std::string> PathCache;
+    static PathCache s_cache;
+    //static float tries = 0, hits = 0;
+
+    std::string cacheKey = relativeTo + "&" + relativePath;
+
+    Threading::ScopedMutexLock lock(s_cacheMutex);
+
+    //tries += 1.0f;
+
+    PathCache::const_iterator i = s_cache.find(cacheKey);
+    if (i != s_cache.end())
+    {
+        //hits += 1.0f;
+        //OE_INFO << "size=" << s_cache.size() <<  " tries=" << tries << " hits=" << (100.*hits/tries) << std::endl;
+        return i->second;
+    }
+
+    // prevent the cache from growing unbounded
+    if (s_cache.size() >= 20000)
+        s_cache.clear();
+
+    // result that will go into the cache:
+    std::string result;
+
 	if (!isRelativePath(relativePath) || relativeTo.empty())
     {
         //OE_NOTICE << relativePath << " is not a relative path " << std::endl;
-        return relativePath;
+        result = relativePath;
     }
 
     //If they didn't specify a relative path, just return the relativeTo
-    if (relativePath.empty()) return relativeTo;
-
-
-    //Note:  Modified from VPB
+    else if (relativePath.empty())
+    {
+        result = relativeTo;
+    }
 
-    //Concatinate the paths together
-    std::string filename;
-    if ( !osgDB::containsServerAddress( relativeTo ) )
-        filename = osgDB::concatPaths( osgDB::getFilePath( osgDB::getRealPath( relativeTo )), relativePath);
     else
-        filename = osgDB::concatPaths( osgDB::getFilePath( relativeTo ), relativePath);
+    {
+        //Note:  Modified from VPB
+
+        //Concatinate the paths together
+        std::string filename;
+        if ( !osgDB::containsServerAddress( relativeTo ) )
+            filename = osgDB::concatPaths( osgDB::getFilePath( osgDB::getRealPath( relativeTo )), relativePath);
+        else
+            filename = osgDB::concatPaths( osgDB::getFilePath( relativeTo ), relativePath);
 
 
-    std::list<std::string> directories;
-    int start = 0;
-    for (unsigned int i = 0; i < filename.size(); ++i)
-    {
-        if (filename[i] == '\\' || filename[i] == '/')
+        std::list<std::string> directories;
+        int start = 0;
+        for (unsigned int i = 0; i < filename.size(); ++i)
         {
-            //Get the current directory
-            std::string dir = filename.substr(start, i-start);
-
-            if (dir != "..")
+            if (filename[i] == '\\' || filename[i] == '/')
             {
-                if (dir != ".")
+                //Get the current directory
+                std::string dir = filename.substr(start, i-start);
+
+                if (dir != "..")
                 {
-                  directories.push_back(dir);
+                    if (dir != ".")
+                    {
+                      directories.push_back(dir);
+                    }
                 }
+                else if (!directories.empty())
+                {
+                    directories.pop_back();
+                }
+                start = i + 1;
             }
-            else if (!directories.empty())
-            {
-                directories.pop_back();
-            }
-            start = i + 1;
         }
-    }
 
-    std::string path;
-    for (std::list<std::string>::iterator itr = directories.begin();
-         itr != directories.end();
-         ++itr)
-    {
-        path += *itr;
-        path += "/";
-    }
+        std::string path;
+        for (std::list<std::string>::iterator itr = directories.begin();
+             itr != directories.end();
+             ++itr)
+        {
+            path += *itr;
+            path += "/";
+        }
+
+        path += filename.substr(start, std::string::npos);
 
-    path += filename.substr(start, std::string::npos);
+        //OE_NOTICE << "FullPath " << path << std::endl;
+        result = path;
+    }
 
-    //OE_NOTICE << "FullPath " << path << std::endl;
-    return path;
+    // cache the result and return it.
+    s_cache[cacheKey] = result;
+    return result;
 }
 
 bool
diff --git a/src/osgEarth/GLSLChunker.cpp b/src/osgEarth/GLSLChunker.cpp
index b778f41..655f517 100644
--- a/src/osgEarth/GLSLChunker.cpp
+++ b/src/osgEarth/GLSLChunker.cpp
@@ -149,7 +149,7 @@ GLSLChunker::read(const std::string& input, Chunks& output) const
                     // if we were in a statement, the precense of an open-brace converts it to a FUNCTION
                     // unless we can detect that it's a struct.
                     if (type == Chunk::TYPE_STATEMENT) {
-                        if (tokens.empty() || tokens[0] != "struct") {
+                        if (tokens.empty() || (tokens[0] != "struct" && tokens[0].substr(0,6) != "layout")) {
                             type = Chunk::TYPE_FUNCTION;
                         }
                     }
diff --git a/src/osgEarth/GPUClamping.frag.glsl b/src/osgEarth/GPUClamping.frag.glsl
index 201178a..4a3ef16 100644
--- a/src/osgEarth/GPUClamping.frag.glsl
+++ b/src/osgEarth/GPUClamping.frag.glsl
@@ -1,4 +1,5 @@
 #version $GLSL_VERSION_STR
+$GLSL_DEFAULT_PRECISION_FLOAT
 
 #pragma vp_entryPoint oe_clamp_fragment
 #pragma vp_location   fragment_coloring
@@ -9,4 +10,4 @@ void oe_clamp_fragment(inout vec4 color)
 {
     // adjust the alpha component to "hide" geometry beyond the visible horizon.
     color.a *= oe_clamp_alpha;
-}
\ No newline at end of file
+}
diff --git a/src/osgEarth/GPUClamping.vert.glsl b/src/osgEarth/GPUClamping.vert.glsl
index c744e88..4a2c3fe 100644
--- a/src/osgEarth/GPUClamping.vert.glsl
+++ b/src/osgEarth/GPUClamping.vert.glsl
@@ -1,25 +1,86 @@
 #version $GLSL_VERSION_STR
+$GLSL_DEFAULT_PRECISION_FLOAT
 
 #pragma vp_entryPoint oe_clamp_vertex
 #pragma vp_location   vertex_view
 #pragma vp_order      0.5
-
+#pragma import_defines(OE_CLAMP_HAS_ATTRIBUTES, OE_GPU_LINES)
 #pragma include GPUClamping.vert.lib.glsl
 
+#ifdef OE_CLAMP_HAS_ATTRIBUTES
 in vec4 oe_clamp_attrs;     // vertex attribute
 in float oe_clamp_height;   // vertex attribute
+#endif
+
+#ifdef OE_GPU_LINES
+in vec3 oe_GPULines_prev;
+in vec3 oe_GPULines_next;
+vec4 oe_GPULines_prevViewClamped;
+vec4 oe_GPULines_nextViewClamped;
+#endif
 
 out float oe_clamp_alpha;
 
-uniform bool oe_clamp_hasAttrs;
 uniform bool oe_isGeocentric;
 uniform float oe_clamp_altitudeOffset;
 uniform float oe_clamp_horizonDistance2;
 
-void oe_clamp_vertex(inout vec4 vertexView)
+
+void oe_clamp_clampViewSpaceVertex(inout vec4 vertexView)
 {
-    const float ClampToAnchor = 1.0;
+#ifdef OE_CLAMP_HAS_ATTRIBUTES
+    bool relativeToAnchor = (oe_clamp_attrs.a == 1.0); // 1.0 = ClampToAnchor
+    float verticalOffset = oe_clamp_attrs.z;
+    float clampHeight = oe_clamp_height;
+
+    // if we are using the anchor point, xform it into view space to prepare
+    // for clamping. Force Z=0 for anchoring.
+    vec4 pointToClamp = relativeToAnchor ?
+        gl_ModelViewMatrix * vec4(oe_clamp_attrs.xy, 0.0, 1.0) :
+        vertexView;
+#else
+    bool relativeToAnchor = false;
+    float verticalOffset = 0.0;
+    vec4 pointToClamp = vertexView;
+    float clampHeight = 0.0;
+#endif
 
+    // clamp the point and remember it's depth:
+    vec4  clampedPoint;
+    float depth;
+    oe_getClampedViewVertex(pointToClamp, clampedPoint, depth);
+
+    float dh = verticalOffset + oe_clamp_altitudeOffset;
+
+    if (relativeToAnchor)
+    {
+        // if we are clamping relative to the anchor point, adjust the HAT based on the
+        // distance from the anchor point to the terrain. Since distance() is unsigned,
+        // we use the vector dot product to calculate whether to adjust up or down.
+        float dist = distance(pointToClamp, clampedPoint);
+        float dir = sign(dot(clampedPoint - pointToClamp, vertexView - pointToClamp));
+        dh += (dist * dir);
+    }
+    else
+    {
+        // if we are clamping to the terrain, the vertex becomes the
+        // clamped point
+        vertexView.xyz = clampedPoint.xyz;
+        dh += clampHeight;
+    }
+
+    // calculate the up vector along which clamping will occur (in either direction)
+    vec3 up;
+    oe_getClampingUpVector(up);
+    vertexView.xyz += up*dh;
+
+    // if the clamped depth value is near the far plane, suppress drawing
+    // to avoid rendering anomalies.
+    oe_clamp_alpha = 1.0 - step(0.9999, depth);
+}
+
+void oe_clamp_vertex(inout vec4 vertexView)
+{
     // check distance; alpha out if its beyone the horizon distance.
     oe_clamp_alpha = oe_isGeocentric ? 
         clamp(oe_clamp_horizonDistance2 - (vertexView.z*vertexView.z), 0.0, 1.0) :
@@ -29,47 +90,14 @@ void oe_clamp_vertex(inout vec4 vertexView)
     // note: no branch divergence in the vertex shader
     if ( oe_clamp_alpha > 0.0 )
     {
-        bool relativeToAnchor = oe_clamp_hasAttrs && (oe_clamp_attrs.a == ClampToAnchor);
-
-        float verticalOffset = oe_clamp_hasAttrs ? oe_clamp_attrs.z : 0.0;
-
-        // if we are using the anchor point, xform it into view space to prepare
-        // for clamping. Force Z=0 for anchoring.
-        vec4 pointToClamp = relativeToAnchor?
-            gl_ModelViewMatrix * vec4(oe_clamp_attrs.xy, 0.0, 1.0) :
-            vertexView;
-
-        // find the clamped point.
-        vec4  clampedPoint;
-        float depth;
-        oe_getClampedViewVertex(pointToClamp, clampedPoint, depth);
-
-        float dh = verticalOffset + oe_clamp_altitudeOffset;
-
-        if ( relativeToAnchor )
-        {
-            // if we are clamping relative to the anchor point, adjust the HAT based on the
-            // distance from the anchor point to the terrain. Since distance() is unsigned,
-            // we use the vector dot product to calculate whether to adjust up or down.
-            float dist = distance(pointToClamp, clampedPoint);
-            float dir  = sign(dot(clampedPoint-pointToClamp, vertexView-pointToClamp));
-            dh += (dist * dir);
-        }
-        else
-        {
-            // if we are clamping to the terrain, the vertex becomes the
-            // clamped point
-            vertexView.xyz = clampedPoint.xyz;
-            dh += oe_clamp_height;
-        }
-        
-        // calculate the up vector along which clamping will occur (in either direction)
-        vec3 up;
-        oe_getClampingUpVector( up );
-        vertexView.xyz += up*dh;
-
-        // if the clamped depth value is near the far plane, suppress drawing
-        // to avoid rendering anomalies.
-        oe_clamp_alpha = 1.0-step(0.9999, depth);
+        oe_clamp_clampViewSpaceVertex(vertexView);
+
+#ifdef OE_GPU_LINES
+        oe_GPULines_prevViewClamped = gl_ModelViewMatrix * vec4(oe_GPULines_prev, 1.0);
+        oe_clamp_clampViewSpaceVertex(oe_GPULines_prevViewClamped);
+
+        oe_GPULines_nextViewClamped = gl_ModelViewMatrix * vec4(oe_GPULines_next, 1.0);
+        oe_clamp_clampViewSpaceVertex(oe_GPULines_nextViewClamped);
+#endif
     }
 }
diff --git a/src/osgEarth/GPUClamping.vert.lib.glsl b/src/osgEarth/GPUClamping.vert.lib.glsl
index a8ca86e..581d1c9 100644
--- a/src/osgEarth/GPUClamping.vert.lib.glsl
+++ b/src/osgEarth/GPUClamping.vert.lib.glsl
@@ -18,7 +18,7 @@ void oe_getClampedViewVertex(in vec4 vertView, out vec4 out_clampedVertView, out
     vec4 vertDepthClip = oe_clamp_cameraView2depthClip * vertView;
 
     // sample the depth map
-    out_depth = texture2DProj( oe_clamp_depthTex, vertDepthClip ).r;
+    out_depth = textureProj( oe_clamp_depthTex, vertDepthClip ).r;
 
     // now transform into depth-view space so we can apply the height-above-ground:
     vec4 clampedVertDepthClip = vec4(vertDepthClip.x, vertDepthClip.y, out_depth, 1.0);
diff --git a/src/osgEarth/GeoCommon b/src/osgEarth/GeoCommon
index 2505b6e..42b8eab 100644
--- a/src/osgEarth/GeoCommon
+++ b/src/osgEarth/GeoCommon
@@ -23,11 +23,11 @@
 #include <limits.h>
 #include <cfloat>
 
-namespace osgEarth
-{
-    // Default normalized no-data value for elevation
+#undef  NO_DATA_VALUE
 #define NO_DATA_VALUE -FLT_MAX
 
+namespace osgEarth
+{
     /**
      * Types of interpolation between two geodetic locations.
      */
diff --git a/src/osgEarth/GeoData b/src/osgEarth/GeoData
index 9f4ee95..2089a5e 100644
--- a/src/osgEarth/GeoData
+++ b/src/osgEarth/GeoData
@@ -24,6 +24,7 @@
 #include <osgEarth/Bounds>
 #include <osgEarth/SpatialReference>
 #include <osgEarth/Units>
+#include <osgEarth/ImageUtils>
 
 #include <osg/Referenced>
 #include <osg/Image>
@@ -168,6 +169,11 @@ namespace osgEarth
         bool transform(const SpatialReference* outSRS, GeoPoint& output) const;
 
         /**
+         * Transforms this point in place to another SRS.
+         */
+        bool transformInPlace(const SpatialReference* srs);
+
+        /**
          * Transforms this geopoint's Z coordinate (in place)
          */
         bool transformZ(const AltitudeMode& altMode, const TerrainResolver* t );
@@ -241,6 +247,11 @@ namespace osgEarth
 
         Config getConfig() const;
 
+        /**
+         * Represent this point as a string
+         */
+        std::string toString() const;
+
     public:
         static GeoPoint INVALID;
 
@@ -337,24 +348,32 @@ namespace osgEarth
         /** dtor */
         virtual ~GeoExtent() { }
 
+        //! Set from the SW and NE corners.
+        void set(double west, double south, double east, double north);
+
         bool operator == ( const GeoExtent& rhs ) const;
         bool operator != ( const GeoExtent& rhs ) const;
 
         /** Gets the spatial reference system underlying this extent. */
         const SpatialReference* getSRS() const { return _srs.get(); }
 
-        double west() const { return _west; }
-        double east() const { return _east; }
-        double south() const { return _south; }
-        double north() const { return _north; }
+        //! Coordinates of the bounding edges, normalized for the lat/long frame if necessary
+        inline double west() const { return _west; }
+        inline double east() const { return normalizeX(_west+_width); }
+        inline double south() const { return _south; }
+        inline double north() const { return _south+_height; }
+
+        //! Coordinates of the bounds, NOT normalized to the lat/long frame.
+        inline double xMin() const { return _west; }
+        inline double xMax() const { return _west + _width; }
+        inline double yMin() const { return _south; }
+        inline double yMax() const { return _south + _height; }
 
-        double xMin() const { return west(); }
-        double xMax() const { return east(); }
-        double yMin() const { return south(); }
-        double yMax() const { return north(); }
+        //! East-to-west span of the extent
+        inline double width() const { return _width; }
 
-        double width() const;
-        double height() const;
+        //! North-to-south span of the extent
+        inline double height() const { return _height; }
 
         /**
          * Gets the centroid of the bounds
@@ -363,20 +382,14 @@ namespace osgEarth
         osg::Vec3d getCentroid() const { osg::Vec3d r; getCentroid(r.x(), r.y()); return r; }
         bool getCentroid( GeoPoint& output ) const;
 
-        /**
-         * Returns true is that extent is in a Geographic (lat/long) SRS that spans
-         * the antimedirian (180 degrees east/west).
-         */
+        //! True if the extent is geographic and crosses the 180 degree meridian.
         bool crossesAntimeridian() const;
 
-        /**
-         * Returns the raw bounds in a single function call
-         */
-        void getBounds(double &xmin, double &ymin, double &xmax, double &ymax) const;
+        //! Raw bounds of the extent (unnormalized)
+        void getBounds(double& xmin, double& ymin, double& xmax, double& ymax) const;
 
         /** True if this object defines a real, valid extent with positive area */
         bool isValid() const;
-        bool defined() const { return isValid(); }
         bool isInvalid() const { return !isValid(); }
 
         /**
@@ -445,7 +458,7 @@ namespace osgEarth
         /**
          * Gets a geo circle bounding this extent.
          */
-        const GeoCircle& getBoundingGeoCircle() const { return _circle; }
+        GeoCircle computeBoundingGeoCircle() const;
 
         /**
          * Grow this extent to include the specified point (which is assumed to be
@@ -487,30 +500,45 @@ namespace osgEarth
         double area() const;
 
         /**
-         * Normalize the longitude values in this GeoExtent
+         * Generate a polytope in world coordinates that bounds the extent.
+         * Return false if the extent it invalid.
          */
-        void normalize();
+        bool createPolytope(osg::Polytope& out) const;
 
         /**
-         * Given a longitude value, normalize it so that it exists within the GeoExtents longitude frame.
+         * Computes a scale/bias matrix that transforms parametric coordinates [0..1]
+         * from this extent into the target extent. Return false if the extents are
+         * incompatible (different SRS, etc.)
+         *
+         * For example, if this extent is 100m wide, and the target extent is
+         * 200m wide, the output matrix will have an x_scale = 0.5.
+         *
+         * Note! For the sake of efficiency, this method does NOT check for 
+         * validity nor for SRS equivalence -- so be sure to validate those beforehand.
+         * It also assumes the output matrix is preset to the identity.
          */
-        double normalizeLongitude( double longitude ) const;
+        bool createScaleBias(const GeoExtent& target, osg::Matrix& output) const;
 
         /**
-         * Generate a polytope in world coordinates that bounds the extent.
-         * Return false if the extent it invalid.
+         * Generates a BoundingSphere encompassing the extent and a vertical 
+         * volume, in world coordinates.
          */
-        bool createPolytope(osg::Polytope& out) const;
+        osg::BoundingSphered createWorldBoundingSphere(double minElev, double maxElev) const;
 
     public:
         static GeoExtent INVALID;
 
     private:
         osg::ref_ptr<const SpatialReference> _srs;
-        double _west, _east, _south, _north;
-        GeoCircle _circle;
+        double _west, _width, _south, _height;
+
+        double normalizeX( double longitude ) const;
 
-        void recomputeCircle();
+        void clamp();
+
+        bool isGeographic() const;
+
+        void setOriginAndSize(double west, double south, double width, double height);
     };
 
     /**
@@ -655,8 +683,30 @@ namespace osgEarth
 
     typedef std::vector<GeoImage> GeoImageVector;
 
+
+    class OSGEARTH_EXPORT NormalMap : public osg::Image
+    {
+    public:
+        NormalMap(unsigned s, unsigned t);
+
+        void set(unsigned s, unsigned t, const osg::Vec3& normal, float curvature =0.0f);
+
+        osg::Vec3 getNormal(unsigned s, unsigned t) const;
+
+        osg::Vec3 getNormalByUV(double u, double v) const;
+
+        float getCurvature(unsigned s, unsigned t) const;
+
+        virtual ~NormalMap();
+
+    private:
+        ImageUtils::PixelWriter* _write;
+        ImageUtils::PixelReader* _read;
+    };
+
+
     /**
-     * A georeferenced heightfield.
+     * A georeferenced heightfield and associated normal/curvature map.
      */
     class OSGEARTH_EXPORT GeoHeightField
     {
@@ -671,6 +721,15 @@ namespace osgEarth
             osg::HeightField* heightField,
             const GeoExtent&  extent );
 
+        //! Constructs a new GeoHeightField
+        //! @param[in] heightField Elevation grid
+        //! @param[in] normalMap   Normal/Curvature map
+        //! @param[in] extent      Geospatial extent of the data
+        GeoHeightField(
+            osg::HeightField* heightField,
+            NormalMap*        normalMap,
+            const GeoExtent&  extent);
+
         /** dtor */
         virtual ~GeoHeightField() { }
 
@@ -709,6 +768,21 @@ namespace osgEarth
             ElevationInterpolation  interp,
             const SpatialReference* srsWithOutputVerticalDatum,
             float&                  out_elevation ) const;
+
+        bool getElevationAndNormal(
+            const SpatialReference* inputSRS, 
+            double                  x,
+            double                  y,
+            ElevationInterpolation  interp,
+            const SpatialReference* srsWithOutputVerticalDatum,
+            float&                  out_elevation,
+            osg::Vec3&              out_normal ) const;
+
+        //! Gets the elevation at a point (must be in the same SRS; bilinear interpolation)
+        float getElevation(double x, double y) const;
+
+        //! Gets the normal at a point (must be in the same SRS; bilinear interpolation)
+        osg::Vec3 getNormal(double x, double y) const;
         
         /**
          * Subsamples the heightfield, returning a new heightfield corresponding to
@@ -737,6 +811,10 @@ namespace osgEarth
         const osg::HeightField* getHeightField() const;
         osg::HeightField* getHeightField();
 
+        //! Normal map
+        const NormalMap* getNormalMap() const;
+        NormalMap* getNormalMap();
+
         /**
          * Gets a pointer to the underlying OSG heightfield, and releases the internal reference.
          */
@@ -764,8 +842,11 @@ namespace osgEarth
 
     protected:
         osg::ref_ptr<osg::HeightField> _heightField;
+        osg::ref_ptr<NormalMap>        _normalMap;
         GeoExtent                      _extent;
         float                          _minHeight, _maxHeight;
+
+        void init();
     };
 
 	typedef std::vector<GeoHeightField> GeoHeightFieldVector;
diff --git a/src/osgEarth/GeoData.cpp b/src/osgEarth/GeoData.cpp
index ca9faf6..09f2ea5 100644
--- a/src/osgEarth/GeoData.cpp
+++ b/src/osgEarth/GeoData.cpp
@@ -28,6 +28,7 @@
 
 #include <osg/Notify>
 #include <osg/Timer>
+#include <osgShadow/ConvexPolyhedron>
 
 #include <gdal_priv.h>
 #include <gdalwarper.h>
@@ -38,74 +39,8 @@
 #include <iomanip>
 #include <cmath>
 
-#define LC "[GeoData] "
-
 using namespace osgEarth;
 
-
-namespace
-{
-    double s_cint( double x )
-    {
-        double dummy;
-        if (modf(x,&dummy) >= .5)
-            return x >= 0.0 ? ceil(x) : floor(x);
-        else
-            return x < 0.0 ? ceil(x) : floor(x);
-    }
-
-    double s_roundNplaces( double x, int n )
-    {
-        double off = pow(10.0, n);
-        return s_cint(x*off)/off;
-    }
-
-    double s_normalizeLongitude( double x, double minLon = -180.0, double maxLon = 180.0 )
-    {
-        double result = x;
-        while( result < minLon ) result += 360.;
-        while( result > maxLon ) result -= 360.;
-        return result;
-    }
-
-    bool s_crossesAntimeridian( double x0, double x1 )
-    {
-        return ((x0 < 0.0 && x1 > 0.0 && x0-x1 < -180.0) ||
-                (x1 < 0.0 && x0 > 0.0 && x1-x0 < -180.0));
-    }
-
-    double s_westToEastLongitudeDistance( double west, double east )
-    {
-        return west < east ? east-west : fmod(east,360.)-west;
-    }
-
-    /**
-     * Given a longitude value determine what longitude frame it is in.
-     * The base longitude frame is -180 to 180.  As values cross the antimeridian the frame is offset by 360 degrees.
-     */
-    void s_getLongitudeFrame( double longitude, double &minLongitude, double &maxLongitude)
-    {
-        minLongitude = -180.0;
-        maxLongitude =  180.0;
-
-        while ( longitude < minLongitude || longitude > maxLongitude)
-        {
-            if (longitude < minLongitude)
-            {
-                minLongitude -= 360.0;
-                maxLongitude -= 360.0;
-            }
-            else if (longitude > maxLongitude)
-            {
-                minLongitude += 360.0;
-                maxLongitude += 360.0;
-            }
-        }
-    }
-}
-
-//------------------------------------------------------------------------
-
 #undef  LC
 #define LC "[GeoPoint] "
 
@@ -319,6 +254,33 @@ GeoPoint::transform(const SpatialReference* outSRS) const
 }
 
 bool
+GeoPoint::transformInPlace(const SpatialReference* srs) 
+{
+    if ( isValid() && srs )
+    {
+        osg::Vec3d out;
+        if ( _altMode == ALTMODE_ABSOLUTE )
+        {
+            if ( _srs->transform(_p, srs, out) )
+            {
+                set(srs, out, ALTMODE_ABSOLUTE);
+                return true;
+            }
+        }
+        else // if ( _altMode == ALTMODE_RELATIVE )
+        {
+            if ( _srs->transform2D(_p.x(), _p.y(), srs, out.x(), out.y()) )
+            {
+                out.z() = _p.z();
+                set(srs, out, ALTMODE_RELATIVE);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool
 GeoPoint::transformZ(const AltitudeMode& altMode, const TerrainResolver* terrain ) 
 {
     double z;
@@ -446,24 +408,26 @@ bool
 GeoPoint::createLocalToWorld( osg::Matrixd& out_l2w ) const
 {
     if ( !isValid() ) return false;
+    bool result = _srs->createLocalToWorld( _p, out_l2w );
     if ( _altMode != ALTMODE_ABSOLUTE )
     {
-        OE_WARN << LC << "ILLEGAL: called GeoPoint::createLocalToorld with AltitudeMode = RELATIVE_TO_TERRAIN" << std::endl;
+        OE_DEBUG << LC << "ILLEGAL: called GeoPoint::createLocalToWorld with AltitudeMode = RELATIVE_TO_TERRAIN" << std::endl;
         return false;
     }
-    return _srs->createLocalToWorld( _p, out_l2w );
+    return result;
 }
 
 bool
 GeoPoint::createWorldToLocal( osg::Matrixd& out_w2l ) const
 {
     if ( !isValid() ) return false;
+    bool result = _srs->createWorldToLocal( _p, out_w2l );
     if ( _altMode != ALTMODE_ABSOLUTE )
     {
-        OE_WARN << LC << "ILLEGAL: called GeoPoint::createWorldToLocal with AltitudeMode = RELATIVE_TO_TERRAIN" << std::endl;
+        OE_DEBUG << LC << "ILLEGAL: called GeoPoint::createWorldToLocal with AltitudeMode = RELATIVE_TO_TERRAIN" << std::endl;
         return false;
     }
-    return _srs->createWorldToLocal( _p, out_w2l );
+    return result;
 }
 
 bool
@@ -525,6 +489,15 @@ GeoPoint::distanceTo(const GeoPoint& rhs) const
     }
 }
 
+std::string
+GeoPoint::toString() const
+{
+    std::stringstream buf;
+    buf << "x=" << x() << ", y=" << y() << ", z=" << z() << "; m=" <<
+        (_altMode == ALTMODE_ABSOLUTE ? "abs" : "rel");
+    return buf.str();
+}
+
 
 //------------------------------------------------------------------------
 
@@ -617,67 +590,115 @@ GeoCircle::intersects( const GeoCircle& rhs ) const
 #undef  LC
 #define LC "[GeoExtent] "
 
+namespace {
+    bool is_valid(double n) {
+        return
+            osg::isNaN(n) == false &&
+            n != DBL_MAX &&
+            n != -DBL_MAX;
+    }
+}
+
 GeoExtent GeoExtent::INVALID = GeoExtent();
 
 
 GeoExtent::GeoExtent():
-_west   ( DBL_MAX ),
-_east   ( DBL_MAX ),
-_south  ( DBL_MAX ),
-_north  ( DBL_MAX )
+_west(0.0),
+_width(-1.0),
+_south(0.0),
+_height(-1.0)
 {
     //NOP - invalid
 }
 
 GeoExtent::GeoExtent(const SpatialReference* srs) :
-_srs    ( srs ),
-_west   ( DBL_MAX ),
-_east   ( DBL_MAX ),
-_south  ( DBL_MAX ),
-_north  ( DBL_MAX )
+_srs(srs),
+_west(0.0),
+_width(-1.0),
+_south(0.0),
+_height(-1.0)
 {
     //NOP - invalid
 }
 
 GeoExtent::GeoExtent(const SpatialReference* srs,
                      double west, double south, double east, double north ) :
-_srs    ( srs ),
-_west   ( west ),
-_east   ( east ),
-_south  ( south ),
-_north  ( north )
+_srs( srs )
 {
-    if ( isValid() )
-        recomputeCircle();
+    set(west, south, east, north);
 }
 
 
-GeoExtent::GeoExtent( const SpatialReference* srs, const Bounds& bounds ) :
-_srs    ( srs ),
-_west   ( bounds.xMin() ),
-_east   ( bounds.xMax() ),
-_south  ( bounds.yMin() ),
-_north  ( bounds.yMax() )
-{    
-    if ( isValid() )
-        recomputeCircle();
+GeoExtent::GeoExtent(const SpatialReference* srs, const Bounds& bounds) :
+_srs( srs )
+{
+    set(bounds.xMin(), bounds.yMin(), bounds.xMax(), bounds.yMax());
 }
 
-GeoExtent::GeoExtent( const GeoExtent& rhs ) :
-_srs   ( rhs._srs ),
-_east  ( rhs._east ),
-_west  ( rhs._west ),
-_south ( rhs._south ),
-_north ( rhs._north ),
-_circle( rhs._circle )
+GeoExtent::GeoExtent(const GeoExtent& rhs) :
+_srs(rhs._srs),
+_west(rhs._west),
+_width(rhs._width),
+_south(rhs._south),
+_height(rhs._height)
 {
     //NOP
 }
 
 bool
+GeoExtent::isGeographic() const
+{
+    return _srs.valid() && _srs->isGeographic();
+}
+
+void
+GeoExtent::set(double west, double south, double east, double north)
+{
+    // Validate input.
+    if (!is_valid(west) ||
+        !is_valid(south) ||
+        !is_valid(east) ||
+        !is_valid(north) ||
+        south > north)
+    {
+        _west = _south = 0.0;
+        _width = _height = -1.0;
+        return;
+    }
+
+    // In this method, east is always to the east of west!
+    // If it appears not to be, that means the extent crosses the antimeridian.
+    west = normalizeX(west);
+    double width = 0.0;
+    double height = 0.0;
+
+    if (isGeographic())
+    {
+        // ensure east >= west in a geographic frame.
+        while (east < west)
+            east += 360.0;
+    }
+    width = std::max(0.0, east - west);
+
+    height = std::max(0.0, north-south);
+
+    setOriginAndSize(west, south, width, height);
+}
+
+void
+GeoExtent::setOriginAndSize(double west, double south, double width, double height)
+{
+    _west = west;
+    _south = south;
+    _width = width;
+    _height = height;
+    clamp();
+}
+
+bool
 GeoExtent::getCentroid(GeoPoint& out) const
 {
-    out = GeoPoint(_srs, getCentroid(), ALTMODE_ABSOLUTE);
+    out = GeoPoint(_srs.get(), getCentroid(), ALTMODE_ABSOLUTE);
     return true;
 }
 
@@ -707,69 +728,39 @@ GeoExtent::operator != ( const GeoExtent& rhs ) const
 bool
 GeoExtent::isValid() const
 {
-    return 
-        _srs.valid()       && 
-        _east  != DBL_MAX  && _east  != -DBL_MAX &&
-        _west  != DBL_MAX  && _west  != -DBL_MAX &&
-        _north != DBL_MAX  && _north != -DBL_MAX &&
-        _south != DBL_MAX  && _south != -DBL_MAX;
-}
-
-double
-GeoExtent::width() const
-{    
-    return crossesAntimeridian() ?
-        (180.0-_west) + (_east+180.0) :
-        _east - _west;
-}
-
-double
-GeoExtent::height() const
-{
-    return _north - _south;
+    return _srs.valid() && _width >= 0.0 && _height >= 0.0;
 }
 
 bool
-GeoExtent::getCentroid( double& out_x, double& out_y ) const
+GeoExtent::getCentroid(double& out_x, double& out_y) const
 {
-    if ( isInvalid() ) return false;
+    if (isInvalid()) return false;
 
+    out_x = normalizeX(west() + 0.5*width());
     out_y = south() + 0.5*height();
-    out_x = west() + 0.5*width();
-
-    if ( _srs->isGeographic() )
-        out_x = normalizeLongitude( out_x );        
     return true;
 }
 
 bool
 GeoExtent::crossesAntimeridian() const
 {
-    return _srs.valid() && _srs->isGeographic() && east() < west();
+    return _srs.valid() && _srs->isGeographic() && east() < west(); //west()+width() > 180.0;
 }
 
 bool
-GeoExtent::splitAcrossAntimeridian( GeoExtent& out_west, GeoExtent& out_east ) const
+GeoExtent::splitAcrossAntimeridian(GeoExtent& out_west, GeoExtent& out_east) const
 {
-    bool success = false;
-
     if ( crossesAntimeridian() )
     {
-        double minLon, maxLon;
-        s_getLongitudeFrame( west(), minLon, maxLon );
-        out_west._srs   = _srs.get();
-        out_west._west  = west();
-        out_west._south = south();
-        out_west._east  = maxLon;
-        out_west._north = north();
+        double width_new;
 
-        out_east._srs   = _srs.get();
-        out_east._west  = minLon;
-        out_east._south = south();
-        out_east._east  = east();
-        out_east._north = north();
+        out_west = *this;
+        width_new = 180.0 - west();
+        out_west.setOriginAndSize(180.0 - width_new, south(), width_new, height());
 
-        success = true;
+        out_east = *this;
+        width_new = east() - (-180.0);
+        out_east.setOriginAndSize(-180.0, south(), width_new, height());
     }
     else if ( !_srs->isGeographic() )
     {
@@ -780,26 +771,26 @@ GeoExtent::splitAcrossAntimeridian( GeoExtent& out_west, GeoExtent& out_east ) c
         {
             out_west = w.transform( _srs.get() );
             out_east = e.transform( _srs.get() );
-            success = out_west.isValid() && out_east.isValid();
         }
     }
-    return success;
+
+    return out_west.isValid() && out_east.isValid();
 }
 
 GeoExtent
-GeoExtent::transform( const SpatialReference* to_srs ) const 
+GeoExtent::transform(const SpatialReference* to_srs) const 
 {       
-    //TODO: this probably doesn't work across the antimeridian
-    if ( _srs.valid() && to_srs )
+    //TODO: this may not work across the antimeridian - unit test required
+    if ( isValid() && to_srs )
     {
+        // do not normalize the X values here.
         double xmin = west(), ymin = south();
-        double xmax = east(), ymax = north();
+        double xmax = west() + width(), ymax = south() + height();
         
-        if ( _srs->transformExtentToMBR( to_srs, xmin, ymin, xmax, ymax ) )
+        if ( _srs->transformExtentToMBR(to_srs, xmin, ymin, xmax, ymax) )
         {
             return GeoExtent( to_srs, xmin, ymin, xmax, ymax );
         }
-
     }
     return GeoExtent::INVALID;
 }
@@ -817,69 +808,70 @@ GeoExtent::getBounds(double &xmin, double &ymin, double &xmax, double &ymax) con
 {
     xmin = west();
     ymin = south();
-    xmax = east();
-    ymax = north();
+    xmax = west() + width();
+    ymax = south() + height();
 }
 
 Bounds
 GeoExtent::bounds() const
 {
-    return Bounds( _west, _south, _east, _north );
+    double west, east, south, north;
+    getBounds(west, south, east, north);
+    return Bounds( west, south, east, north );
 }
 
 bool
 GeoExtent::contains(double x, double y, const SpatialReference* srs) const
 {
-    if ( isInvalid() )
+    if (isInvalid() || !is_valid(x) || !is_valid(y))
         return false;
 
     osg::Vec3d xy( x, y, 0 );
-    osg::Vec3d localxy = xy;
+    osg::Vec3d local = xy;
 
-    if (srs &&
-        !srs->isEquivalentTo( _srs.get() ) &&
-        !srs->transform(xy, _srs.get(), localxy) )
-    {    
-        return false;
-    }
-    else
+    // See if we need to xform the input:
+    if (srs && srs->isHorizEquivalentTo(_srs.get()) == false)
     {
-        // normalize a geographic longitude to -180:+180
-        if ( _srs->isGeographic() )
-            localxy.x() = normalizeLongitude( localxy.x() );            
-
-        //Account for small rounding errors along the edges of the extent
-        if (osg::equivalent(_west, localxy.x())) localxy.x() = _west;
-        if (osg::equivalent(_east, localxy.x())) localxy.x() = _east;
-        if (osg::equivalent(_south, localxy.y())) localxy.y() = _south;
-        if (osg::equivalent(_north, localxy.y())) localxy.y() = _north;
-
-        if ( crossesAntimeridian() )
-        {
-            if ( localxy.x() > 0.0 )
-            {
-                return localxy.x() >= _west && localxy.x() <= 180.0 && localxy.y() >= _south && localxy.y() <= _north;
-            }
-            else
-            {
-                return localxy.x() >= -180.0 && localxy.x() <= _east && localxy.y() >= _south && localxy.y() <= _north;
-            }
-        }
-        else
+        // If the transform fails, bail out with error
+        if (srs->transform(xy, _srs.get(), local) == false)
         {
-            return localxy.x() >= _west && localxy.x() <= _east && localxy.y() >= _south && localxy.y() <= _north;
+            return false;
         }
     }
+
+    // Quantize the Y coordinate to account for tiny rounding errors:
+    if (osg::equivalent(south(), local.y()))
+        local.y() = south();
+    if (osg::equivalent(north(), local.y()))
+        local.y() = north();
+
+    // Test the Y coordinate:
+    if (local.y() < south() || local.y() > north())
+        return false;
+
+    // Bring the X coordinate into normal range:
+    local.x() = normalizeX(local.x());
+    
+    // Quantize the X coordinate to account for tiny rounding errors:
+    if (osg::equivalent(west(), local.x()))
+        local.x() = west();
+    if (osg::equivalent(east(), local.x()))
+        local.x() = east();
+
+    // account for the antimeridian wrap-around:
+    double a0 = west(), a1 = west() + width();
+    double b0 = east() - width(), b1 = east();
+    return (a0 <= local.x() && local.x() <= a1) || (b0 <= local.x() && local.x() <= b1);
 }
 
 bool
-GeoExtent::contains( const GeoPoint& rhs ) const
+GeoExtent::contains(const GeoPoint& rhs) const
 {
     return contains( rhs.x(), rhs.y(), rhs.getSRS() );
 }
 
 bool
-GeoExtent::contains( const Bounds& rhs ) const
+GeoExtent::contains(const Bounds& rhs) const
 {
     return
         isValid() &&
@@ -890,59 +882,76 @@ GeoExtent::contains( const Bounds& rhs ) const
 }
 
 bool
-GeoExtent::contains( const GeoExtent& rhs ) const
+GeoExtent::contains(const GeoExtent& rhs) const
 {
     return
         isValid() &&
         rhs.isValid() &&
-        contains( rhs.west(), rhs.south() ) &&
-        contains( rhs.east(), rhs.north() ) &&
-        contains( rhs.getCentroid() );   // this accounts for the antimeridian
+        contains( rhs.west(), rhs.south(), rhs.getSRS() ) &&
+        contains( rhs.east(), rhs.north(), rhs.getSRS() ) &&
+        contains( rhs.getCentroid(), rhs.getSRS() );   // this accounts for the antimeridian
 }
 
+#undef  OVERLAPS
+#define OVERLAPS(A, B, C, D) (!(B <= C || A >= D))
+
 bool
-GeoExtent::intersects( const GeoExtent& rhs, bool checkSRS ) const
+GeoExtent::intersects(const GeoExtent& rhs, bool checkSRS) const
 {
     if ( !isValid() || !rhs.isValid() )
         return false;
 
+    // Transform the incoming extent if necessary:
     if ( checkSRS && !_srs->isHorizEquivalentTo(rhs.getSRS()) )
     {
-        GeoExtent rhsExt = rhs.transform(getSRS());
-        return this->intersects( rhsExt );
+        if (_srs->isContiguous())
+        {
+            GeoExtent rhsExt = rhs.transform(getSRS());
+            return this->intersects( rhsExt, false );
+        }
+        else
+        {
+            // non-contiguous projection? convert to a contiguous one:
+            GeoExtent thisGeo = transform(getSRS()->getGeographicSRS());
+            GeoExtent rhsGeo = rhs.transform(getSRS()->getGeographicSRS());
+            return thisGeo.intersects(rhsGeo, false);
+        }
     }
 
-    if ( rhs.crossesAntimeridian() )
-    {
-        GeoExtent rhsWest, rhsEast;
-        rhs.splitAcrossAntimeridian( rhsWest, rhsEast );
-        return rhsWest.intersects(*this) || rhsEast.intersects(*this);
-    }
-    else if ( crossesAntimeridian() )
+    // Trivial reject: y-dimension does not overlap:
+    bool y_excl = south() >= rhs.north() || north() <= rhs.south();
+    if (y_excl)
+        return false;
+
+    // Trivial reject: x-dimension does not overlap in projected SRS:
+    if (!_srs->isGeographic())
     {
-        GeoExtent west, east;
-        splitAcrossAntimeridian(west, east);
-        return rhs.intersects(west) || rhs.intersects(east);
+        bool x_excl = west() >= rhs.east() || east() <= rhs.west();
+        return x_excl == false;
     }
-    else
-    {
-        bool exclusive =
-            _west >= rhs.east() ||
-            _east <= rhs.west() ||
-            _south >= rhs.north() ||
-            _north <= rhs.south();
 
-        return !exclusive;
-    }
+    // By now we know that Y overlaps and we are in a geographic SRS
+    // and therefore must consider the antimeridian wrap-around in X.
+    // a and b are "this"; c and d are "rhs":
+    double a0 = east() - width(), a1 = east();
+    double b0 = west(), b1 = west() + width();
+    double c0 = rhs.east() - rhs.width(), c1 = rhs.east();
+    double d0 = rhs.west(), d1 = rhs.west() + rhs.width();
+    return
+        OVERLAPS(a0, a1, c0, c1) ||
+        OVERLAPS(a0, a1, d0, d1) ||
+        OVERLAPS(b0, b1, c0, c1) ||
+        OVERLAPS(b0, b1, d0, d1);
 }
 
-
-void
-GeoExtent::recomputeCircle()
+GeoCircle
+GeoExtent::computeBoundingGeoCircle() const
 {
+    GeoCircle circle;
+
     if ( !isValid() )
     {
-        _circle.setRadius( -1.0 );
+        circle.setRadius( -1.0 );
     }
     else 
     {
@@ -952,8 +961,7 @@ GeoExtent::recomputeCircle()
         if ( getSRS()->isProjected() )
         {
             double ext = std::max( width(), height() );
-            _circle.setRadius( 0.5*ext * 1.414121356237 ); /*sqrt(2)*/
-            //_circle.setRadius( (osg::Vec2d(x,y)-osg::Vec2d(_west,_south)).length() );
+            circle.setRadius( 0.5*ext * 1.414121356237 ); /*sqrt(2)*/
         }
         else // isGeographic
         {
@@ -970,113 +978,103 @@ GeoExtent::recomputeCircle()
             radius2 = std::max(radius2, (center-ne).length2());
             radius2 = std::max(radius2, (center-sw).length2());
 
-            _circle.setRadius( sqrt(radius2) );
-#if 0
-            double extDegrees;
-            double metersPerDegree = (getSRS()->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI) / 360.0;
-
-            if ( width() > height() )
-            {
-                extDegrees = width();
-                double widestLatitude = std::min( fabs(north()), fabs(south()) );
-                metersPerDegree *= cos(osg::DegreesToRadians(widestLatitude));
-            }
-            else
-            {
-                extDegrees = height();
-            }
-
-            double extMeters = extDegrees * metersPerDegree;
-            _circle.setRadius( 0.5*extMeters * 1.414121356237 ); /*sqrt(2)*/
-#endif
+            circle.setRadius( sqrt(radius2) );
         }
 
-        _circle.setCenter( GeoPoint(getSRS(), x, y, 0.0, ALTMODE_ABSOLUTE) );
+        circle.setCenter( GeoPoint(getSRS(), x, y, 0.0, ALTMODE_ABSOLUTE) );
     }
-}
 
+    return circle;
+}
 
 void
-GeoExtent::expandToInclude( double x, double y )
+GeoExtent::expandToInclude(double x, double y)
 {
-    if ( west() == DBL_MAX )
-    {
-        _west = x;
-        _east = x;
-        _south = y;
-        _north = y;
-    }
-    else if ( getSRS() && getSRS()->isGeographic() )
-    {
-        x = normalizeLongitude( x );
+    if (!is_valid(x) || !is_valid(y))
+        return;
 
-        // calculate possible expansion distances. The lesser of the two
-        // will be the direction in which we expand.
+    // First, bring the X coordinate into the local frame.
+    x = normalizeX(x);
 
-        // west:
-        double dw;
-        if ( x > west() )
-            dw = west() - (x-360.);
-        else
-            dw = west() - x;
+    // Invalid? Set to a point.
+    if (isInvalid())
+    {
+        set(x, y, x, y);
+        return;
+    }
 
-        // east:
-        double de;
-        if ( x < east() )
-            de = (x+360.) - east();
-        else
-            de = x - east();
+    // Check each coordinate separately:
+    double cx, cy;
+    getCentroid(cx, cy);
+    bool containsX = contains(x, cy);
+    bool containsY = contains(cx, y);
 
-        // this is the empty space available - growth beyond this 
-        // automatically yields full extent [-180..180]
-        double maxWidth = 360.0-width();
+    // Expand along the Y axis:
+    if (!containsY)
+    {
+        if (y < south())
+        {
+            _height += (_south-y);
+            _south = y;
+        }
+        else if (y > north())
+        {
+            _height = y - south();
+        }
+    }
 
-        // if both are > 180, then the point is already in our extent.
-        if ( dw <= 180. || de <= 180. )
+    if (!containsX)
+    {
+        if (isGeographic())
         {
-            if ( dw < de )
+            if (x > west())
             {
-                if ( dw < maxWidth )
+                double w0 = x - west(); // non-wrap-around width
+                double w1 = (180.0 - x) + (west() - (-180.0) + _width); // wrap-around width
+                if (w0 <= w1)
                 {
-                    // expand westward
-                    _west -= dw;                    
-                    _west = normalizeLongitude( _west );
+                    _width = w0;
                 }
                 else
                 {
-                    // reached full extent
-                    _west = -180.0;
-                    _east =  180.0;
+                    _west = x;
+                    _width = w1;
                 }
             }
-            else
+            else // (x < west())
             {
-                if ( de < maxWidth )
+                double w0 = _width + (west() - x); // non-wrap-around
+                double w1 = (x - (-180.0)) + (180.0 - west()); // wrap-around
+                if (w0 < w1)
                 {
-                    // expand eastward
-                    _east += de;
-                    _east = normalizeLongitude(_east);
+                    _west = x;
+                    _width = w0;
                 }
                 else
                 {
-                    // reached full extent.
-                    _west = -180.0;
-                    _east =  180.0;
+                    _width = w1;
                 }
             }
         }
-        //else already inside longitude extent
+        else
+        {
+            // projected mode is the same approach as Y
+            if (x < west())
+            {
+                _width += _west - x;
+                _west = x;
+            }
+            else if (x > east())
+            {
+                _width = x - west();
+            }
+        }
     }
-    else
+
+    if (!containsX || !containsY)
     {
-        _west = std::min(_west, x);
-        _east = std::max(_east, x);
+        clamp();
     }
-
-    _south = std::min(_south, y);
-    _north = std::max(_north, y);
-
-    recomputeCircle();
 }
 
 void
@@ -1088,27 +1086,42 @@ GeoExtent::expandToInclude(const Bounds& rhs)
 }
 
 bool
-GeoExtent::expandToInclude( const GeoExtent& rhs )
+GeoExtent::expandToInclude(const GeoExtent& rhs)
 {
-    if ( isInvalid() || rhs.isInvalid() ) return false;
+    if ( isInvalid() || rhs.isInvalid() )
+        return false;
 
-    if ( !rhs.getSRS()->isEquivalentTo( _srs.get() ) )
+    if ( !rhs.getSRS()->isHorizEquivalentTo( _srs.get() ) )
     {
-        return expandToInclude( transform(rhs.getSRS()) );
+        return expandToInclude( rhs.transform(_srs.get()) );
     }
+
     else
     {
-        // include the centroid first in order to honor an 
-        // antimeridian-crossing profile
+        // include the centroid first in order to get the optimal
+        // expansion direction.
         expandToInclude( rhs.getCentroid() );
         expandToInclude( rhs.west(), rhs.south() );
         expandToInclude( rhs.east(), rhs.north() );
-        return true;
+    }
+
+    return true;
+}
+
+namespace
+{
+    void sort4(double* n, bool* b)
+    {
+        if (n[0] > n[1]) std::swap(n[0], n[1]), std::swap(b[0], b[1]);
+        if (n[2] > n[3]) std::swap(n[2], n[3]), std::swap(b[2], b[3]);
+        if (n[0] > n[2]) std::swap(n[0], n[2]), std::swap(b[0], b[2]);
+        if (n[1] > n[3]) std::swap(n[1], n[3]), std::swap(b[1], b[3]);
+        if (n[1] > n[2]) std::swap(n[1], n[2]), std::swap(b[1], b[2]);
     }
 }
 
 GeoExtent
-GeoExtent::intersectionSameSRS( const GeoExtent& rhs ) const
+GeoExtent::intersectionSameSRS(const GeoExtent& rhs) const
 {
     if ( isInvalid() || rhs.isInvalid() || !_srs->isHorizEquivalentTo( rhs.getSRS() ) )
         return GeoExtent::INVALID;
@@ -1122,31 +1135,69 @@ GeoExtent::intersectionSameSRS( const GeoExtent& rhs ) const
 
     GeoExtent result( *this );
 
-    double westAngle, eastAngle;
-    
-    // see if the rhs western boundary intersects our extent:
-    westAngle = s_westToEastLongitudeDistance( west(), rhs.west() );
-    eastAngle = s_westToEastLongitudeDistance( rhs.west(), east() );
-    if ( westAngle < width() && eastAngle < width() ) // yes, so adjust the result eastward:
+    if (isGeographic())
     {
-        result._west += westAngle;
-    }
+        if (width() == 360.0)
+        {
+            result._west = rhs._west;
+            result._width = rhs._width;
+        }
+        else if (rhs.width() == 360.0)
+        {
+            result._west = _west;
+            result._width = _width;
+        }
+        else
+        {
+            // Sort the four X coordinates, remembering whether each one is west or east edge:
+            double x[4];
+            bool iswest[4];
+            x[0] = west(), x[1] = east(), x[2] = rhs.west(), x[3] = rhs.east();
+            iswest[0] = true, iswest[1] = false, iswest[2] = true, iswest[3] = false;
+            sort4(x, iswest);
+
+            // find the western-most west coord:
+            int iw = -1;
+            for (int i=0; i<4 && iw<0; ++i)
+            {
+                if (iswest[i])
+                    iw = i;
+            }
+
+            // iterate from there, finding the LAST west coord and stopping on the 
+            // FIRST east coord found.
+            int q = iw+4;
+            int ie = -1;
+            for (int i = iw; i < q && ie < 0; ++i)
+            {
+                int j = i;
+                if (j >= 4) j-=4;
+                if (iswest[j])
+                    iw = j; // found a better west coord; remember it.
+                else
+                    ie = j; // found the western-most east coord; done.
+            }
 
-    // now see if the rhs eastern boundary intersects out extent:
-    westAngle = s_westToEastLongitudeDistance( west(), rhs.east() );
-    eastAngle = s_westToEastLongitudeDistance( rhs.east(), east() );
-    if ( westAngle < width() && eastAngle < width() ) // yes, so adjust again:
+            result._west = x[iw];
+            if (ie >= iw)
+                result._width = x[ie] - x[iw];
+            else
+                result._width = (180.0 - x[iw]) + (x[ie] - (-180.0)); // crosses the antimeridian
+        }
+    }
+    else
     {
-        result._east -= eastAngle;
+        // projected mode is simple
+        result._west = std::max(west(), rhs.west());
+        double eastTemp = std::min(east(), rhs.east());
+        result._width = eastTemp - result._west;
     }
 
-    // normalize our new longitudes
-    result._west = normalizeLongitude( result._west );
-    result._east = normalizeLongitude( result._east );
+    result._south = std::max(south(), rhs.south());
+    double northTemp = std::min(north(), rhs.north());
+    result._height = northTemp - result._south;
 
-    // latitude is easy, just clamp it
-    result._south = std::max( south(), rhs.south() );
-    result._north = std::min( north(), rhs.north() );
+    result.clamp();
 
     OE_DEBUG << "Intersection of " << this->toString() << " and " << rhs.toString() << " is: " 
         << result.toString()
@@ -1158,38 +1209,71 @@ GeoExtent::intersectionSameSRS( const GeoExtent& rhs ) const
 void
 GeoExtent::scale(double x_scale, double y_scale)
 {
-    if ( isInvalid() )
+    if (isInvalid() || !is_valid(x_scale) || !is_valid(y_scale))
         return;
 
-    double orig_width = width();
-    double orig_height = height();
+    double cx = _west + 0.5*_width;
 
-    double new_width  = orig_width  * x_scale;
-    double new_height = orig_height * y_scale;
-
-    double halfXDiff = (new_width - orig_width) / 2.0;
-    double halfYDiff = (new_height - orig_height) /2.0;
+    double cy = _south + 0.5*_height;
+     
+    setOriginAndSize(
+        normalizeX(cx - 0.5*_width*x_scale),
+        cy - 0.5*_height*y_scale,
+        _width * x_scale,
+        _height * y_scale);
+}
 
-    _west  -= halfXDiff;
-    _east  += halfXDiff;
-    _south -= halfYDiff;
-    _north += halfYDiff;
+void
+GeoExtent::expand(double x, double y)
+{
+    if (isInvalid() || !is_valid(x) || !is_valid(y))
+        return;
 
-    recomputeCircle();
+    setOriginAndSize(
+        normalizeX(_west - 0.5*x),
+        _south - 0.5*y,
+        _width + x,
+        _height + y);
 }
 
 void
-GeoExtent::expand( double x, double y )
+GeoExtent::clamp()
 {
-    if ( isInvalid() )
-        return;
+    if (osg::equivalent(_west, floor(_west)))
+        _west = floor(_west);
+    else if (osg::equivalent(_west, ceil(_west)))
+        _west = ceil(_west);
 
-    _west  -= .5*x;
-    _east  += .5*x;
-    _south -= .5*y;
-    _north += .5*y;
+    if (osg::equivalent(_south, floor(_south)))
+        _south = floor(_south);
+    else if (osg::equivalent(_south, ceil(_south)))
+        _south = ceil(_south);
 
-    recomputeCircle();
+    if (osg::equivalent(_width, floor(_width)))
+        _width = floor(_width);
+    else if (osg::equivalent(_width, ceil(_width)))
+        _width = ceil(_width);
+
+    if (osg::equivalent(_height, floor(_height)))
+        _height = floor(_height);
+    else if (osg::equivalent(_height, ceil(_height)))
+        _height = ceil(_height);
+
+    if (isGeographic())
+    {
+        _width = osg::clampBetween(_width, 0.0, 360.0);
+        _height = osg::clampBetween(_height, 0.0, 180.0);
+
+        if (south() < -90.0)
+        {
+            _height -= (-90.0)-_south;
+            _south = -90.0;
+        }
+        else if (north() > 90.0)
+        {
+            _height -= (north()-90.0);            
+        }
+    }
 }
 
 double
@@ -1198,26 +1282,17 @@ GeoExtent::area() const
     return isValid() ? width() * height() : 0.0;
 }
 
-void
-GeoExtent::normalize()
-{
-    if (isValid() && _srs->isGeographic())
-    {
-        _west = s_normalizeLongitude( _west );
-        _east = s_normalizeLongitude( _east );
-    }
-}
-
 double
-GeoExtent::normalizeLongitude( double longitude ) const
+GeoExtent::normalizeX(double x) const
 {
-    if (isValid() && _srs->isGeographic())
+    if (isValid() && is_valid(x) && _srs->isGeographic())
     {
-        double minLon, maxLon;
-        s_getLongitudeFrame( _west, minLon, maxLon );        
-        return s_normalizeLongitude( longitude, minLon, maxLon );
+        while (x < -180.0)
+            x += 360.0;
+        while ( x > 180.0 )
+            x -= 360.0;
     }
-    return longitude;
+    return x;
 }
 
 std::string
@@ -1267,10 +1342,10 @@ GeoExtent::createPolytope(osg::Polytope& tope) const
         // convert 4 corners to world space (ECEF)
         osg::Vec3d center(0.0, 0.0, 0.0);
         osg::Vec3d sw, se, nw, ne;
-        GeoPoint(getSRS(), _west, _south, 0.0, ALTMODE_ABSOLUTE).toWorld( sw );
-        GeoPoint(getSRS(), _east, _south, 0.0, ALTMODE_ABSOLUTE).toWorld( se );
-        GeoPoint(getSRS(), _east, _north, 0.0, ALTMODE_ABSOLUTE).toWorld( ne );
-        GeoPoint(getSRS(), _west, _north, 0.0, ALTMODE_ABSOLUTE).toWorld( nw );
+        GeoPoint(getSRS(), west(), south(), 0.0, ALTMODE_ABSOLUTE).toWorld( sw );
+        GeoPoint(getSRS(), east(), south(), 0.0, ALTMODE_ABSOLUTE).toWorld( se );
+        GeoPoint(getSRS(), east(), north(), 0.0, ALTMODE_ABSOLUTE).toWorld( ne );
+        GeoPoint(getSRS(), west(), north(), 0.0, ALTMODE_ABSOLUTE).toWorld( nw );
 
         // bounding planes in ECEF space:
         tope.add( osg::Plane(center, nw, sw) ); // west
@@ -1282,23 +1357,67 @@ GeoExtent::createPolytope(osg::Polytope& tope) const
     return true;
 }
 
+osg::BoundingSphered
+GeoExtent::createWorldBoundingSphere(double minElev, double maxElev) const
+{
+    osg::BoundingSphered bs;
+
+    if (getSRS()->isProjected())
+    {
+        osg::Vec3d w;
+        GeoPoint(getSRS(), xMin(), yMin(), minElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMax(), yMax(), maxElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+    }
+
+    else // geocentric
+    {
+        osg::Vec3d w;
+        GeoPoint(getSRS(), xMin(), yMin(), minElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMax(), yMin(), minElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMax(), yMax(), minElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMin(), yMax(), minElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMin(), yMin(), maxElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMax(), yMin(), maxElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMax(), yMax(), maxElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+        GeoPoint(getSRS(), xMin(), yMax(), maxElev, ALTMODE_ABSOLUTE).toWorld(w); bs.expandBy(w);
+    }
+
+    return bs;
+}
+
+bool
+GeoExtent::createScaleBias(const GeoExtent& rhs, osg::Matrix& output) const
+{    
+    double scalex = width() / rhs.width();
+    double scaley = height() / rhs.height();
+    double biasx  = (west()-rhs.west()) / rhs.width();
+    double biasy  = (south()-rhs.south()) / rhs.height();
+
+    output(0,0) = scalex;
+    output(1,1) = scaley;
+    output(3,0) = biasx;
+    output(3,1) = biasy;
+
+    return true;
+}
+
 /***************************************************************************/
 
-DataExtent::DataExtent(const osgEarth::GeoExtent& extent, unsigned minLevel,  unsigned maxLevel) :
+DataExtent::DataExtent(const GeoExtent& extent, unsigned minLevel,  unsigned maxLevel) :
 GeoExtent(extent)
 {
     _minLevel = minLevel;
     _maxLevel = maxLevel;
 }
 
-DataExtent::DataExtent(const osgEarth::GeoExtent& extent, unsigned minLevel) :
+DataExtent::DataExtent(const GeoExtent& extent, unsigned minLevel) :
 GeoExtent(extent),
 _maxLevel( 25 )
 {
     _minLevel = minLevel;
 }
 
-DataExtent::DataExtent(const osgEarth::GeoExtent& extent ) :
+DataExtent::DataExtent(const GeoExtent& extent ) :
 GeoExtent(extent),
 _minLevel( 0 ),
 _maxLevel( 25 )
@@ -1734,9 +1853,6 @@ namespace
             height = osg::minimum(image->s(), image->t());
         }
 
-        // need to know this in order to choose the right interpolation algorithm
-        const bool isSrcContiguous = src_extent.getSRS()->isContiguous();
-
         osg::Image *result = new osg::Image();
         //result->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
         result->allocateImage(width, height, 1, image->getPixelFormat(), image->getDataType()); //GL_UNSIGNED_BYTE);
@@ -1762,6 +1878,7 @@ namespace
         // the sample grid into the source coordinate system.
         double *srcPointsX = new double[numPixels * 2];
         double *srcPointsY = srcPointsX + numPixels;
+
         dest_extent.getSRS()->transformExtentPoints(
             src_extent.getSRS(),
             dest_extent.xMin() + .5 * dx, dest_extent.yMin() + .5 * dy,
@@ -1985,6 +2102,125 @@ GeoImage::takeImage()
     return _image.release();
 }
 
+/***************************************************************************/
+
+#define DEFAULT_NORMAL osg::Vec3(0,0,1)
+#define DEFAULT_CURVATURE 0.0f
+
+NormalMap::NormalMap(unsigned s, unsigned t) :
+osg::Image(),
+_write(0L),
+_read(0L)
+{
+    const osg::Vec3 defaultNormal(DEFAULT_NORMAL);
+    const float defaultCurvature(DEFAULT_CURVATURE);
+
+    if ( s > 0 && t > 0 )
+    {
+        allocateImage(s, t, 1, GL_RGBA, GL_UNSIGNED_BYTE, 1);
+
+        _write = new ImageUtils::PixelWriter(this);
+        _read = new ImageUtils::PixelReader(this);
+
+        for (unsigned y=0; y<t; ++y)
+            for (unsigned x=0; x<s; ++x)
+                set(x, y, defaultNormal, defaultCurvature);
+    }
+}
+
+NormalMap::~NormalMap()
+{
+    if (_read) delete _read;
+    if (_write) delete _write;
+}
+
+void
+NormalMap::set(unsigned s, unsigned t, const osg::Vec3& normal, float curvature)
+{
+    if (!_write) return;
+
+    osg::Vec4f encoding(
+        0.5f*(normal.x()+1.0f),
+        0.5f*(normal.y()+1.0f),
+        0.5f*(normal.z()+1.0f),
+        0.5f*(curvature+1.0f));
+
+    (*_write)(encoding, s, t);
+}
+
+osg::Vec3
+NormalMap::getNormal(unsigned s, unsigned t) const
+{
+    if (!_read) return osg::Vec3(0,0,1);
+
+    osg::Vec4 encoding = (*_read)(s, t);
+    return osg::Vec3(
+        encoding.x()*2.0 - 1.0,
+        encoding.y()*2.0 - 1.0,
+        encoding.z()*2.0 - 1.0);
+}
+
+osg::Vec3
+NormalMap::getNormalByUV(double u, double v) const
+{
+    if (!_read) return osg::Vec3(0,0,1);
+
+    double c = u * (double)(s()-1);
+    double r = v * (double)(t()-1);
+
+    unsigned rowMin = osg::maximum((int)floor(r), 0);
+    unsigned rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(t() - 1)), 0);
+    unsigned colMin = osg::maximum((int)floor(c), 0);
+    unsigned colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(s() - 1)), 0);
+    
+    if (rowMin > rowMax) rowMin = rowMax;
+    if (colMin > colMax) colMin = colMax;
+
+    osg::Vec3 ur = getNormal(colMax, rowMax);
+    osg::Vec3 ll = getNormal(colMin, rowMin);
+    osg::Vec3 ul = getNormal(colMin, rowMax);
+    osg::Vec3 lr = getNormal(colMax, rowMin);
+
+    osg::Vec3 result;
+
+    // Bilinear:
+    //if (interpolation == INTERP_BILINEAR)
+    {
+        //Check for exact value
+        if ((colMax == colMin) && (rowMax == rowMin))
+        {
+            // exact
+            result = getNormal(colMin, rowMin);
+        }
+        else if (colMax == colMin)
+        {
+            //Linear interpolate vertically
+            result = ll*((double)rowMax - r) + ul*(r - (double)rowMin);
+        }
+        else if (rowMax == rowMin)
+        {
+            //Linear interpolate horizontally
+            result = ll*((double)colMax - c) + lr*(c - (double)colMin);
+        }
+        else
+        {
+            //Bilinear interpolate
+            osg::Vec3 n1 = ll*((double)colMax - c) + lr*(c - (double)colMin);
+            osg::Vec3 n2 = ul*((double)colMax - c) + ur*(c - (double)colMin);
+            result = n1*((double)rowMax - r) + n2*(r - (double)rowMin);
+        }
+    }
+
+    result.normalize();
+    return result;
+}
+
+float
+NormalMap::getCurvature(unsigned s, unsigned t) const
+{
+    if (!_read) return 0.0f;
+    return (*_read)(s, t).a() * 2.0f - 1.0f;
+}
 
 /***************************************************************************/
 
@@ -2010,7 +2246,25 @@ _extent     ( extent ),
 _minHeight( FLT_MAX ),
 _maxHeight( -FLT_MAX )
 {
-    if ( _heightField.valid() && extent.isInvalid() )
+    init();
+}
+
+GeoHeightField::GeoHeightField(osg::HeightField* heightField,
+                               NormalMap*        normalMap,
+                               const GeoExtent&  extent) :
+_heightField( heightField ),
+_normalMap  ( normalMap ),
+_extent     ( extent ),
+_minHeight  ( FLT_MAX ),
+_maxHeight  ( -FLT_MAX )
+{
+    init();
+}
+
+void
+GeoHeightField::init()
+{
+    if ( _heightField.valid() && _extent.isInvalid() )
     {
         OE_WARN << LC << "Created with a valid heightfield AND INVALID extent" << std::endl;
     }
@@ -2041,6 +2295,24 @@ GeoHeightField::valid() const
     return _heightField.valid() && _extent.isValid();
 }
 
+float
+GeoHeightField::getElevation(double x, double y) const
+{
+    return HeightFieldUtils::getHeightAtLocation(
+        _heightField.get(),
+        x, y,
+        _extent.xMin(), _extent.yMin(),
+        _heightField->getXInterval(), _heightField->getYInterval(),
+        INTERP_BILINEAR);
+}
+
+osg::Vec3
+GeoHeightField::getNormal(double x, double y) const
+{
+    return !_normalMap.valid() ? osg::Vec3(0,0,1) :
+        _normalMap->getNormalByUV((x - _extent.xMin()) / _extent.width(), (y - _extent.yMin()) / _extent.height());
+}
+
 bool
 GeoHeightField::getElevation(const SpatialReference* inputSRS, 
                              double                  x, 
@@ -2055,6 +2327,71 @@ GeoHeightField::getElevation(const SpatialReference* inputSRS,
 
 
     // first xform the input point into our local SRS:
+    if (inputSRS != extentSRS)
+    {
+        if (inputSRS && !inputSRS->transform(xy, extentSRS, local))
+            return false;
+    }
+
+    // check that the point falls within the heightfield bounds:
+    if ( _extent.contains(local.x(), local.y()) )
+    {
+        double xInterval = _extent.width()  / (double)(_heightField->getNumColumns()-1);
+        double yInterval = _extent.height() / (double)(_heightField->getNumRows()-1);
+
+        // sample the heightfield at the input coordinates:
+        // (note: since it's sampling the HF, it will return an MSL height if applicable)
+        out_elevation = HeightFieldUtils::getHeightAtLocation(
+            _heightField.get(), 
+            local.x(), local.y(),
+            _extent.xMin(), _extent.yMin(), 
+            xInterval, yInterval, 
+            interp);
+
+        // if the vertical datums don't match, do a conversion:
+        if (out_elevation != NO_DATA_VALUE && 
+            outputSRS && 
+            !extentSRS->isVertEquivalentTo(outputSRS) )
+        {
+            // if the caller provided a custom output SRS, perform the appropriate
+            // Z transformation. This requires a lat/long point:
+
+            osg::Vec3d geolocal(local);
+            if ( !extentSRS->isGeographic() )
+            {
+                extentSRS->transform(geolocal, extentSRS->getGeographicSRS(), geolocal);
+            }
+
+            VerticalDatum::transform(
+                extentSRS->getVerticalDatum(),
+                outputSRS->getVerticalDatum(),
+                geolocal.y(), geolocal.x(), out_elevation);
+        }
+
+        return true;
+    }
+    else
+    {
+        out_elevation = 0.0f;
+        return false;
+    }
+}
+
+bool
+GeoHeightField::getElevationAndNormal(const SpatialReference* inputSRS,
+                                      double                  x,
+                                      double                  y,
+                                      ElevationInterpolation  interp,
+                                      const SpatialReference* outputSRS,
+                                      float&                  out_elevation,
+                                      osg::Vec3f&             out_normal) const
+{
+    osg::Vec3d xy(x, y, 0);
+    osg::Vec3d local = xy;
+    const SpatialReference* extentSRS = _extent.getSRS();
+
+
+    // first xform the input point into our local SRS:
     if ( inputSRS && !inputSRS->transform(xy, extentSRS, local) )
         return false;
 
@@ -2093,11 +2430,34 @@ GeoHeightField::getElevation(const SpatialReference* inputSRS,
                 geolocal.y(), geolocal.x(), out_elevation);
         }
 
+        // If we have a normal map, use it; if not, attempt to generate a normal
+        // by sampling the heightfield.
+        if (_normalMap.valid())
+        {
+            //OE_INFO << "Normal Map Exists\n";
+            double nx = osg::clampBetween((local.x() - _extent.xMin()) / _extent.width(), 0.0, 1.0);
+            double ny = osg::clampBetween((local.y() - _extent.yMin()) / _extent.height(), 0.0, 1.0);
+            out_normal = _normalMap->getNormalByUV(nx, ny);
+        }
+        else
+        {
+            HeightFieldNeighborhood hood;
+            hood.setNeighbor(0, 0, _heightField.get());
+
+            // calculate the normal at the same location:
+            out_normal = HeightFieldUtils::getNormalAtLocation(
+                hood,
+                local.x(), local.y(),
+                _extent.xMin(), _extent.yMin(), 
+                xInterval, yInterval);
+        }
+
         return true;
     }
     else
     {
         out_elevation = 0.0f;
+        out_normal.set(0.0f, 0.0f, 1.0f);
         return false;
     }
 }
@@ -2126,9 +2486,9 @@ GeoHeightField::createSubSample( const GeoExtent& destEx, unsigned int width, un
     double xstep = div / (double)(width-1);
     double ystep = div / (double)(height-1);
     
-    for( x = x0, col = 0; col < width; x += xstep, col++ )
+    for( x = x0, col = 0; col < (int)width; x += xstep, col++ )
     {
-        for( y = y0, row = 0; row < height; y += ystep, row++ )
+        for( y = y0, row = 0; row < (int)height; y += ystep, row++ )
         {
             float height = HeightFieldUtils::getHeightAtNormalizedLocation(
                 _heightField.get(), x, y, interpolation );
@@ -2163,6 +2523,18 @@ GeoHeightField::takeHeightField()
     return _heightField.release();
 }
 
+NormalMap*
+GeoHeightField::getNormalMap()
+{
+    return _normalMap.get();
+}
+
+const NormalMap*
+GeoHeightField::getNormalMap() const
+{
+    return _normalMap.get();
+}
+
 double
 GeoHeightField::getXInterval() const
 {
diff --git a/src/osgEarth/GeoTransform b/src/osgEarth/GeoTransform
index 25b0639..4f91030 100644
--- a/src/osgEarth/GeoTransform
+++ b/src/osgEarth/GeoTransform
@@ -71,6 +71,7 @@ namespace osgEarth
          * a reference terrain (see setTerrain).
          */
         void setAutoRecomputeHeights(bool value);
+        bool getAutoRecomputeHeights() const { return _autoRecomputeHeights; }
 
     public:
         /** Callback that lets the user intercept the compute*Matrix functions during a traversal. */
@@ -99,6 +100,12 @@ namespace osgEarth
                 : osg::MatrixTransform::computeWorldToLocalMatrix(m, nv);
         }
 
+
+    public: // osg::Node
+
+        virtual void traverse(osg::NodeVisitor& nv);
+
+
     public: // TerrainCallback interface
 
         // called when new data pages in and autoRecompute is true
@@ -106,13 +113,16 @@ namespace osgEarth
                          osg::Node*              node,
                          TerrainCallbackContext& context);
 
+
     protected:
         virtual ~GeoTransform() { }
 
-        GeoPoint                   _position;
-        osg::observer_ptr<Terrain> _terrain;
-        bool                       _autoRecompute;
-        bool                       _autoRecomputeReady;
+        GeoPoint                   _position;                 // Current position
+        osg::observer_ptr<Terrain> _terrain;                  // Terrain for relative height resolution
+        bool                       _terrainCallbackInstalled; // Whether the Terrain callback is in
+        bool                       _findTerrain;              // True is we need _terrain but don't have it
+        bool                       _autoRecomputeHeights;     // Whether to resolve relative position Z's
+        bool                       _dirtyClamp;               // Whether a terrain clamp is required
 
         osg::ref_ptr<ComputeMatrixCallback> _computeMatrixCallback;
 
diff --git a/src/osgEarth/GeoTransform.cpp b/src/osgEarth/GeoTransform.cpp
index afb4437..a784495 100644
--- a/src/osgEarth/GeoTransform.cpp
+++ b/src/osgEarth/GeoTransform.cpp
@@ -18,6 +18,8 @@
  */
 #include <osgEarth/GeoTransform>
 #include <osgEarth/Terrain>
+#include <osgEarth/MapNode>
+#include <osgEarth/NodeUtils>
 
 #define LC "[GeoTransform] "
 
@@ -26,39 +28,39 @@
 using namespace osgEarth;
 
 GeoTransform::GeoTransform() :
-_autoRecompute     ( false ),
-_autoRecomputeReady( false )
+_findTerrain(false),
+_terrainCallbackInstalled(false),
+_autoRecomputeHeights(true),
+_dirtyClamp(false)
 {
    //nop
 }
 
-GeoTransform::GeoTransform(const GeoTransform& rhs,
-                           const osg::CopyOp&  op) :
+GeoTransform::GeoTransform(const GeoTransform& rhs, const osg::CopyOp& op) :
 osg::MatrixTransform(rhs, op)
 {
-    _position           = rhs._position;
-    _terrain            = rhs._terrain.get();
-    _autoRecompute      = rhs._autoRecompute;
-    _autoRecomputeReady = false;
+    _position = rhs._position;
+    _terrain = rhs._terrain.get();
+    _autoRecomputeHeights = rhs._autoRecomputeHeights;
+    _terrainCallbackInstalled = false;
+    _findTerrain = false;
+    _dirtyClamp = rhs._dirtyClamp;
 }
 
 void
 GeoTransform::setTerrain(Terrain* terrain)
 {
     _terrain = terrain;
-
-    // Change in the terrain means we need to recompute the position
-    // if one is set.
-    if ( _position.isValid() )
-        setPosition( _position );
+    setPosition(_position);
 }
 
 void
 GeoTransform::setAutoRecomputeHeights(bool value)
 {
-    if (value != _autoRecompute)
+    if (value != _autoRecomputeHeights)
     {
-        _autoRecompute = value;
+        _autoRecomputeHeights = value;
+        setPosition(_position);
     }
 }
 
@@ -74,26 +76,33 @@ GeoTransform::setPosition(const GeoPoint& position)
     if ( !position.isValid() )
         return false;
 
+    bool result = true;
+
     _position = position;
 
     // relative Z or reprojection require a terrain:
     osg::ref_ptr<Terrain> terrain;
     _terrain.lock(terrain);
 
-    // relative Z requires a terrain:
-    if (position.altitudeMode() == ALTMODE_RELATIVE && !terrain.valid())
+    // If we don't have a pointer to a terrain, schedule an attempt
+    // to find one on the next update traversal.
+    if (!terrain.valid() && !_findTerrain)
     {
-        OE_TEST << LC << "setPosition failed condition 1\n";
-        return false;
+        _findTerrain = true;
+        ADJUST_UPDATE_TRAV_COUNT(this, +1);
     }
 
     GeoPoint p;
 
     // transform into terrain SRS if neccesary:
     if (terrain.valid() && !terrain->getSRS()->isEquivalentTo(position.getSRS()))
+    {
         p = position.transform(terrain->getSRS());
+    }
     else
+    {
         p = position;
+    }
 
     // bail if the transformation failed:
     if ( !p.isValid() )
@@ -102,31 +111,30 @@ GeoTransform::setPosition(const GeoPoint& position)
         return false;
     }
 
-    // convert to absolute height:
-    if ( !p.makeAbsolute(_terrain.get()) )
+    // Convert the point to an absolute Z if necessry. If we don't have
+    // a terrain, skip and hope for the best.
+    if (terrain.valid())
     {
-        OE_TEST << LC << "setPosition failed condition 3\n";
-        return false;
+        result = p.makeAbsolute(terrain.get()) && result;
+    }
+
+    // Is this is a relative-Z position, we need to install a terrain callback
+    // so we can recompute the altitude when new terrain tiles become available.
+    if (_position.altitudeMode() == ALTMODE_RELATIVE &&
+        _autoRecomputeHeights &&
+        !_terrainCallbackInstalled &&
+        terrain.valid())
+    {
+        // The Adapter template auto-destructs, so we never need to remote it manually.
+        terrain->addTerrainCallback( new TerrainCallbackAdapter<GeoTransform>(this) );
+        _terrainCallbackInstalled = true;
     }
 
-    // assemble the matrix:
+    // Finally, assemble the matrix from our position point.
     osg::Matrixd local2world;
     p.createLocalToWorld( local2world );
     this->setMatrix( local2world );
 
-    // install auto-recompute?
-    if (_autoRecompute &&
-        _position.altitudeMode() == ALTMODE_RELATIVE &&
-        !_autoRecomputeReady)
-    {
-        // by using the adapter, there's no need to remove
-        // the callback then this object destructs.
-        terrain->addTerrainCallback(
-           new TerrainCallbackAdapter<GeoTransform>(this) );
-
-        _autoRecomputeReady = true;
-    }
-
     return true;
 }
 
@@ -135,19 +143,53 @@ GeoTransform::onTileAdded(const TileKey&          key,
                           osg::Node*              node,
                           TerrainCallbackContext& context)
 {
-   if (!_position.isValid() || _position.altitudeMode() != ALTMODE_RELATIVE)
-   {
-       OE_TEST << LC << "onTileAdded fail condition 1\n";
-       return;
-   }
-
-   if (!key.getExtent().contains(_position))
-   {
-       OE_DEBUG << LC << "onTileAdded fail condition 2\n";
-       return;
-   }
-
-   setPosition(_position);
+    if (!_dirtyClamp)
+    {
+       if (!_position.isValid() || _position.altitudeMode() != ALTMODE_RELATIVE || !_autoRecomputeHeights)
+       {
+           OE_TEST << LC << "onTileAdded fail condition 1\n";
+           return;
+       }
+
+       if (key.valid() && !key.getExtent().contains(_position))
+       {
+           OE_TEST << LC << "onTileAdded fail condition 2\n";
+           return;
+       }
+
+       _dirtyClamp = true;
+       ADJUST_UPDATE_TRAV_COUNT(this, +1);
+    }
+
+    //setPosition(_position);
+}
+
+void
+GeoTransform::traverse(osg::NodeVisitor& nv)
+{
+    if (nv.getVisitorType() == nv.UPDATE_VISITOR)
+    {
+        if (_findTerrain)
+        {
+            MapNode* mapNode = osgEarth::findInNodePath<MapNode>(nv);
+            if (mapNode)
+            {
+                _findTerrain = false;
+                ADJUST_UPDATE_TRAV_COUNT(this, -1);
+                setTerrain(mapNode->getTerrain());
+                OE_DEBUG << LC << "Discovered terrain.\n";
+            }
+        }
+
+        if (_dirtyClamp)
+        {
+            setPosition(_position);
+            _dirtyClamp = false;
+            ADJUST_UPDATE_TRAV_COUNT(this, -1);
+        }
+    }
+
+    osg::MatrixTransform::traverse(nv);
 }
 
 void
@@ -156,6 +198,7 @@ GeoTransform::setComputeMatrixCallback(GeoTransform::ComputeMatrixCallback* cb)
     _computeMatrixCallback = cb;
 }
 
+
 bool
 GeoTransform::ComputeMatrixCallback::computeLocalToWorldMatrix(const GeoTransform* xform, osg::Matrix& m, osg::NodeVisitor* nv) const
 {
diff --git a/src/osgEarth/GeometryClamper b/src/osgEarth/GeometryClamper
index c033fd5..ca7c2ff 100644
--- a/src/osgEarth/GeometryClamper
+++ b/src/osgEarth/GeometryClamper
@@ -23,7 +23,7 @@
 #include <osgEarth/Common>
 #include <osgEarth/SpatialReference>
 #include <osgEarth/Terrain>
-#include <osgEarth/DPLineSegmentIntersector>
+#include <osgUtil/LineSegmentIntersector>
 #include <osg/NodeVisitor>
 #include <osg/fast_back_stack>
 
@@ -32,6 +32,7 @@ namespace osgEarth
     /**
      * Utility that takes existing OSG geometry and modifies it so that
      * it "conforms" with a terrain patch.
+     * TODO: Consider relocating this into Annotation namespace
      */
     class OSGEARTH_EXPORT GeometryClamper : public osg::NodeVisitor
     {
@@ -57,7 +58,7 @@ namespace osgEarth
 
     public: // osg::NodeVisitor
 
-        void apply( osg::Geode& );
+        void apply( osg::Drawable& );
         void apply( osg::Transform& );
 
     protected:
@@ -68,7 +69,7 @@ namespace osgEarth
         float                                _scale;
         float                                _offset;
         osg::fast_back_stack<osg::Matrixd>   _matrixStack;
-        osg::ref_ptr<DPLineSegmentIntersector> _lsi;
+        osg::ref_ptr<osgUtil::LineSegmentIntersector> _lsi;
     };
 
 
diff --git a/src/osgEarth/GeometryClamper.cpp b/src/osgEarth/GeometryClamper.cpp
index 686a434..8a6dab1 100644
--- a/src/osgEarth/GeometryClamper.cpp
+++ b/src/osgEarth/GeometryClamper.cpp
@@ -39,7 +39,7 @@ _scale          ( 1.0f ),
 _offset         ( 0.0f )
 {
     this->setNodeMaskOverride( ~0 );
-    _lsi = new osgEarth::DPLineSegmentIntersector(osg::Vec3d(0,0,0), osg::Vec3d(0,0,0));
+    _lsi = new osgUtil::LineSegmentIntersector(osg::Vec3d(0,0,0), osg::Vec3d(0,0,0));
 }
 
 void
@@ -54,11 +54,15 @@ GeometryClamper::apply(osg::Transform& xform)
 }
 
 void
-GeometryClamper::apply(osg::Geode& geode)
+GeometryClamper::apply(osg::Drawable& drawable)
 {
     if ( !_terrainSRS.valid() )
         return;
 
+    osg::Geometry* geom = drawable.asGeometry();
+    if ( !geom )
+        return;
+
     const osg::Matrixd& local2world = _matrixStack.back();
     osg::Matrix world2local;
     world2local.invert( local2world );
@@ -74,113 +78,106 @@ GeometryClamper::apply(osg::Geode& geode)
 
     unsigned count = 0;
 
-    for( unsigned i=0; i<geode.getNumDrawables(); ++i )
+    bool geomDirty = false;
+    osg::Vec3Array*  verts = static_cast<osg::Vec3Array*>(geom->getVertexArray());
+    osg::FloatArray* zOffsets = 0L;
+
+    // if preserve-Z is on, check for our elevations array. Create it if is doesn't
+    // already exist.
+    bool buildZOffsets = false;
+    if ( _preserveZ )
     {
-        bool geomDirty = false;
-        osg::Geometry* geom = geode.getDrawable(i)->asGeometry();
-        if ( geom )
+        osg::UserDataContainer* udc = geom->getOrCreateUserDataContainer();
+        unsigned n = udc->getUserObjectIndex( ZOFFSETS_NAME );
+        if ( n < udc->getNumUserObjects() )
         {
-            osg::Vec3Array*  verts = static_cast<osg::Vec3Array*>(geom->getVertexArray());
-            osg::FloatArray* zOffsets = 0L;
+            zOffsets = dynamic_cast<osg::FloatArray*>(udc->getUserObject(n));
+        }
 
-            // if preserve-Z is on, check for our elevations array. Create it if is doesn't
-            // already exist.
-            bool buildZOffsets = false;
-            if ( _preserveZ )
-            {
-                osg::UserDataContainer* udc = geom->getOrCreateUserDataContainer();
-                unsigned n = udc->getUserObjectIndex( ZOFFSETS_NAME );
-                if ( n < udc->getNumUserObjects() )
-                {
-                    zOffsets = dynamic_cast<osg::FloatArray*>(udc->getUserObject(n));
-                }
+        else
+        {
+            zOffsets = new osg::FloatArray();
+            zOffsets->setName( ZOFFSETS_NAME );
+            zOffsets->reserve( verts->size() );
+            udc->addUserObject( zOffsets );
+            buildZOffsets = true;
+        }
+    }
 
-                else
-                {
-                    zOffsets = new osg::FloatArray();
-                    zOffsets->setName( ZOFFSETS_NAME );
-                    zOffsets->reserve( verts->size() );
-                    udc->addUserObject( zOffsets );
-                    buildZOffsets = true;
-                }
-            }
+    for( unsigned k=0; k<verts->size(); ++k )
+    {
+        osg::Vec3d vw = (*verts)[k];
+        vw = vw * local2world;
+
+        if ( isGeocentric )
+        {
+            // normal to the ellipsoid:
+            n_vector = em->computeLocalUpVector(vw.x(),vw.y(),vw.z());
 
-            for( unsigned k=0; k<verts->size(); ++k )
+            // if we need to build to z-offsets array, calculate the z offset now:
+            if ( buildZOffsets || _scale != 1.0 )
             {
-                osg::Vec3d vw = (*verts)[k];
-                vw = vw * local2world;
+                double lat,lon,hae;
+                em->convertXYZToLatLongHeight(vw.x(), vw.y(), vw.z(), lat, lon, hae);
 
-                if ( isGeocentric )
+                if ( buildZOffsets )
                 {
-                    // normal to the ellipsoid:
-                    n_vector = em->computeLocalUpVector(vw.x(),vw.y(),vw.z());
-
-                    // if we need to build to z-offsets array, calculate the z offset now:
-                    if ( buildZOffsets || _scale != 1.0 )
-                    {
-                        double lat,lon,hae;
-                        em->convertXYZToLatLongHeight(vw.x(), vw.y(), vw.z(), lat, lon, hae);
-
-                        if ( buildZOffsets )
-                        {
-                            zOffsets->push_back( (*verts)[k].z() );
-                        }
-
-                        if ( _scale != 1.0 )
-                        {
-                            msl = vw - n_vector*hae;
-                        }
-                    }
+                    zOffsets->push_back( hae ); //(*verts)[k].z() );
                 }
 
-                else if ( buildZOffsets ) // flat map
+                if ( _scale != 1.0 )
                 {
-                    zOffsets->push_back( float(vw.z()) );
+                    msl = vw - n_vector*hae;
                 }
+            }
+        }
 
-                _lsi->reset();
-                _lsi->setStart( vw + n_vector*r*_scale );
-                _lsi->setEnd( vw - n_vector*r );
-                _lsi->setIntersectionLimit( _lsi->LIMIT_NEAREST );
+        else if ( buildZOffsets ) // flat map
+        {
+            zOffsets->push_back( float(vw.z()) );
+        }
 
-                _terrainPatch->accept( iv );
+        _lsi->reset();
+        _lsi->setStart( vw + n_vector*r*_scale );
+        _lsi->setEnd( vw - n_vector*r );
+        _lsi->setIntersectionLimit( _lsi->LIMIT_NEAREST );
 
-                if ( _lsi->containsIntersections() )
-                {
-                    osg::Vec3d fw = _lsi->getFirstIntersection().getWorldIntersectPoint();
-                    if ( _scale != 1.0 )
-                    {
-                        osg::Vec3d delta = fw - msl;
-                        fw += delta*_scale;
-                    }
-                    if ( _offset != 0.0 )
-                    {
-                        fw += n_vector*_offset;
-                    }
-                    if ( _preserveZ && (zOffsets != 0L) )
-                    {
-                        fw += n_vector * (*zOffsets)[k];
-                    }
-
-                    (*verts)[k] = (fw * world2local);
-                    geomDirty = true;
-                    ++count;
-                }
-            }
+        _terrainPatch->accept( iv );
 
-            if ( geomDirty )
+        if ( _lsi->containsIntersections() )
+        {
+            osg::Vec3d fw = _lsi->getFirstIntersection().getWorldIntersectPoint();
+            if ( _scale != 1.0 )
             {
-                geom->dirtyBound();
-                if ( geom->getUseVertexBufferObjects() )
-                {
-                    verts->getVertexBufferObject()->setUsage( GL_DYNAMIC_DRAW_ARB );
-                    verts->dirty();
-                }
-                else
-                {
-                    geom->dirtyDisplayList();
-                }
+                osg::Vec3d delta = fw - msl;
+                fw += delta*_scale;
+            }
+            if ( _offset != 0.0 )
+            {
+                fw += n_vector*_offset;
+            }
+            if ( _preserveZ && (zOffsets != 0L) )
+            {
+                fw += n_vector * (*zOffsets)[k];
             }
+
+            (*verts)[k] = (fw * world2local);
+            geomDirty = true;
+            ++count;
+        }
+    }
+
+    if ( geomDirty )
+    {
+        geom->dirtyBound();
+        if ( geom->getUseVertexBufferObjects() )
+        {
+            verts->getVertexBufferObject()->setUsage( GL_DYNAMIC_DRAW_ARB );
+            verts->dirty();
+        }
+        else
+        {
+            geom->dirtyDisplayList();
         }
 
         OE_DEBUG << LC << "clamped " << count << " verts." << std::endl;
@@ -188,7 +185,6 @@ GeometryClamper::apply(osg::Geode& geode)
 }
 
 
-
 void
 GeometryClamperCallback::onTileAdded(const TileKey&          key, 
                                      osg::Node*              tile, 
diff --git a/src/osgEarth/HTTPClient b/src/osgEarth/HTTPClient
index bd64272..777ffdc 100644
--- a/src/osgEarth/HTTPClient
+++ b/src/osgEarth/HTTPClient
@@ -99,8 +99,10 @@ namespace osgEarth
         /** Ready-only access to the parameter list (as built with addParameter) */
         const Parameters& getParameters() const;        
 
+        //! Add a head name/value pair to an HTTP request
         void addHeader( const std::string& name, const std::string& value );
 
+        //! Collection of headers in this request
         const Headers& getHeaders() const;
 
         /**
@@ -173,7 +175,9 @@ namespace osgEarth
         const std::string& getMimeType() const;
 
         /** How long did it take to fetch this response (in seconds) */
-        double getDuration() const { return _duration_s; }        
+        double getDuration() const { return _duration_s; }     
+
+        const std::string& getMessage() const { return _message; }
 
     private:
         struct Part : public osg::Referenced
@@ -190,6 +194,7 @@ namespace osgEarth
         bool        _cancelled;
         double      _duration_s;
         TimeStamp   _lastModified;
+        std::string _message;
 
         Config getHeadersAsConfig() const;
 
diff --git a/src/osgEarth/HTTPClient.cpp b/src/osgEarth/HTTPClient.cpp
index eeececa..55bc4cb 100644
--- a/src/osgEarth/HTTPClient.cpp
+++ b/src/osgEarth/HTTPClient.cpp
@@ -1,1658 +1,1704 @@
-/* -*-c++-*- */
-/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
- * Copyright 2016 Pelican Mapping
- * http://osgearth.org
- *
- * osgEarth is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>
- */
-#include <osgEarth/HTTPClient>
-#include <osgEarth/Registry>
-#include <osgEarth/Version>
-#include <osgEarth/Progress>
-#include <osgEarth/StringUtils>
-#include <osgDB/ReadFile>
-#include <osgDB/Registry>
-#include <osgDB/FileNameUtils>
-#include <osg/Notify>
-#include <osg/Timer>
-#include <string.h>
-#include <sstream>
-#include <fstream>
-#include <iterator>
-#include <iostream>
-#include <algorithm>
-#include <curl/curl.h>
-
-// Whether to use WinInet instead of cURL - CMAKE option
-#ifdef OSGEARTH_USE_WININET_FOR_HTTP
-#include <WinInet.h>
-#pragma comment(lib, "wininet.lib")
-#endif
-
-#define LC "[HTTPClient] "
-
-//#define OE_TEST OE_NOTICE
-#define OE_TEST OE_NULL
-
-using namespace osgEarth;
-
-//----------------------------------------------------------------------------
-
-ProxySettings::ProxySettings( const Config& conf )
-{
-    mergeConfig( conf );
-}
-
-ProxySettings::ProxySettings( const std::string& host, int port ) :
-_hostName(host),
-_port(port)
-{
-    //nop
-}
-
-void
-ProxySettings::mergeConfig( const Config& conf )
-{
-    _hostName = conf.value<std::string>( "host", "" );
-    _port = conf.value<int>( "port", 8080 );
-    _userName = conf.value<std::string>( "username", "" );
-    _password = conf.value<std::string>( "password", "" );
-}
-
-Config
-ProxySettings::getConfig() const
-{
-    Config conf( "proxy" );
-    conf.add( "host", _hostName );
-    conf.add( "port", toString(_port) );
-    conf.add( "username", _userName);
-    conf.add( "password", _password);
-
-    return conf;
-}
-
-bool
-ProxySettings::fromOptions( const osgDB::Options* dbOptions, optional<ProxySettings>& out )
-{
-    if ( dbOptions )
-    {
-        std::string jsonString = dbOptions->getPluginStringData( "osgEarth::ProxySettings" );
-        if ( !jsonString.empty() )
-        {
-            Config conf;
-            conf.fromJSON( jsonString );
-            out = ProxySettings( conf );
-            return true;
-        }
-    }
-    return false;
-}
-
-void
-ProxySettings::apply( osgDB::Options* dbOptions ) const
-{
-    if ( dbOptions )
-    {
-        Config conf = getConfig();
-        dbOptions->setPluginStringData( "osgEarth::ProxySettings", conf.toJSON() );
-    }
-}
-
-/****************************************************************************/
-   
-namespace osgEarth
-{
-    struct StreamObject
-    {
-        StreamObject(std::ostream* stream) : _stream(stream) { }
-
-        void write(const char* ptr, size_t realsize)
-        {
-            if (_stream) _stream->write(ptr, realsize);
-        }
-
-        void writeHeader(const char* ptr, size_t realsize)
-        {            
-            std::string header(ptr);            
-            StringTokenizer tok(":");
-            StringVector tized;
-            tok.tokenize(header, tized);            
-            if ( tized.size() >= 2 )
-                _headers[tized[0]] = tized[1];                
-        }
-
-        std::ostream* _stream;
-        Headers _headers;
-        std::string     _resultMimeType;
-    };
-
-    static size_t
-    StreamObjectReadCallback(void* ptr, size_t size, size_t nmemb, void* data)
-    {
-        size_t realsize = size* nmemb;
-        StreamObject* sp = (StreamObject*)data;
-        sp->write((const char*)ptr, realsize);
-        return realsize;
-    }
-
-    static size_t
-    StreamObjectHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data)
-    {
-        size_t realsize = size* nmemb;
-        StreamObject* sp = (StreamObject*)data;                
-        sp->writeHeader((const char*)ptr, realsize);        
-        return realsize;
-    }
-
-    TimeStamp
-    getCurlFileTime(void* curl)
-    {
-        long filetime;
-        if (CURLE_OK != curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime))
-            return TimeStamp(0);
-        else if (filetime < 0)
-            return TimeStamp(0);
-        else
-            return TimeStamp(filetime);
-    }
-}
-
-static int CurlProgressCallback(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
-{
-    ProgressCallback* callback = (ProgressCallback*)clientp;
-    bool cancelled = false;
-    if (callback)
-    {
-        cancelled = callback->isCanceled() || callback->reportProgress(dlnow, dltotal);
-    }
-    return cancelled;
-}
-
-/****************************************************************************/
-
-HTTPRequest::HTTPRequest( const std::string& url )
-: _url( url )
-{
-    //NOP
-}
-
-HTTPRequest::HTTPRequest( const HTTPRequest& rhs ) :
-_parameters( rhs._parameters ),
-_headers(rhs._headers),
-_url( rhs._url )
-{
-    //nop
-}
-
-void
-HTTPRequest::addParameter( const std::string& name, const std::string& value )
-{
-    _parameters[name] = value;
-}
-
-void
-HTTPRequest::addParameter( const std::string& name, int value )
-{
-    std::stringstream buf;
-    buf << value;
-     std::string bufStr;
-    bufStr = buf.str();
-    _parameters[name] = bufStr;
-}
-
-void
-HTTPRequest::addParameter( const std::string& name, double value )
-{
-    std::stringstream buf;
-    buf << value;
-     std::string bufStr;
-    bufStr = buf.str();
-    _parameters[name] = bufStr;
-}
-
-const HTTPRequest::Parameters&
-HTTPRequest::getParameters() const
-{
-    return _parameters; 
-}
-
-void
-HTTPRequest::addHeader( const std::string& name, const std::string& value )
-{
-    _headers[name] = value;
-}
-
-const Headers&
-HTTPRequest::getHeaders() const
-{
-    return _headers; 
-}
-
-void HTTPRequest::setLastModified( const DateTime &lastModified)
-{    
-    addHeader("If-Modified-Since", lastModified.asRFC1123());
-}
-
-
-std::string
-HTTPRequest::getURL() const
-{
-    if ( _parameters.size() == 0 )
-    {
-        return _url;
-    }
-    else
-    {
-        std::stringstream buf;
-        buf << _url;
-        for( Parameters::const_iterator i = _parameters.begin(); i != _parameters.end(); i++ )
-        {
-            buf << ( i == _parameters.begin() && _url.find( "?" ) == std::string::npos? "?" : "&" );
-            buf << i->first << "=" << i->second;
-        }
-         std::string bufStr;
-         bufStr = buf.str();
-        return bufStr;
-    }
-}
-
-/****************************************************************************/
-
-HTTPResponse::HTTPResponse( long _code ) :
-_response_code( _code ),
-_cancelled(false),
-_duration_s(0.0),
-_lastModified(0u)
-{
-    _parts.reserve(1);
-}
-
-HTTPResponse::HTTPResponse( const HTTPResponse& rhs ) :
-_response_code( rhs._response_code ),
-_parts( rhs._parts ),
-_mimeType( rhs._mimeType ),
-_cancelled( rhs._cancelled ),
-_duration_s(0.0),
-_lastModified(0u)
-{
-    //nop
-}
-
-unsigned
-HTTPResponse::getCode() const {
-    return _response_code;
-}
-
-bool
-HTTPResponse::isOK() const {
-    return _response_code == 200L && !isCancelled();
-}
-
-bool
-HTTPResponse::isCancelled() const {
-    return _cancelled;
-}
-
-unsigned int
-HTTPResponse::getNumParts() const {
-    return _parts.size();
-}
-
-unsigned int
-HTTPResponse::getPartSize( unsigned int n ) const {
-    return _parts[n]->_size;
-}
-
-const std::string&
-HTTPResponse::getPartHeader( unsigned int n, const std::string& name ) const {
-    return _parts[n]->_headers[name];
-}
-
-std::istream&
-HTTPResponse::getPartStream( unsigned int n ) const {
-    return _parts[n]->_stream;
-}
-
-std::string
-HTTPResponse::getPartAsString( unsigned int n ) const {
-    std::string streamStr;
-    streamStr = _parts[n]->_stream.str();    
-    return streamStr;
-}
-
-const std::string&
-HTTPResponse::getMimeType() const {
-    return _mimeType;
-}
-
-Config
-HTTPResponse::getHeadersAsConfig() const
-{
-    Config conf;
-    if ( _parts.size() > 0 )
-    {
-        for( Headers::const_iterator i = _parts[0]->_headers.begin(); i != _parts[0]->_headers.end(); ++i )
-        {
-            conf.set(i->first, i->second);
-        }
-    }
-    return conf;
-}
-
-/****************************************************************************/
-
-#define QUOTE_(X) #X
-#define QUOTE(X) QUOTE_(X)
-#define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION)
-
-
-namespace
-{
-    // TODO: consider moving this stuff into the osgEarth::Registry;
-    // don't like it here in the global scope
-    // per-thread client map (must be global scope)
-    static PerThread<HTTPClient>       s_clientPerThread;
-
-    static optional<ProxySettings>     s_proxySettings;
-
-    static std::string                 s_userAgent = USER_AGENT;
-
-    static long                        s_timeout = 0;
-    static long                        s_connectTimeout = 0;
-
-    // HTTP debugging.
-    static bool                        s_HTTP_DEBUG = false;
-    static Threading::Mutex            s_HTTP_DEBUG_mutex;
-    static int                         s_HTTP_DEBUG_request_count;
-    static double                      s_HTTP_DEBUG_total_duration;
-
-    static osg::ref_ptr< URLRewriter > s_rewriter;
-
-    static osg::ref_ptr< CurlConfigHandler > s_curlConfigHandler;
-}
-
-HTTPClient&
-HTTPClient::getClient()
-{
-    return s_clientPerThread.get();
-}
-
-HTTPClient::HTTPClient() :
-_initialized    ( false ),
-_curl_handle    ( 0L ),
-_simResponseCode( -1L ),
-_previousHttpAuthentication(0L)
-{
-    //nop
-    //do no CURL calls here.
-}
-
-void
-HTTPClient::initialize() const
-{
-    if ( !_initialized )
-    {
-        const_cast<HTTPClient*>(this)->initializeImpl();
-    }
-}
-
-void
-HTTPClient::initializeImpl()
-{
-    _previousHttpAuthentication = 0;
-    _curl_handle = curl_easy_init();
-
-    //Get the user agent
-    std::string userAgent = s_userAgent;
-    const char* userAgentEnv = getenv("OSGEARTH_USERAGENT");
-    if (userAgentEnv)
-    {
-        userAgent = std::string(userAgentEnv);
-    }
-
-    //Check for a response-code simulation (for testing)
-    const char* simCode = getenv("OSGEARTH_SIMULATE_HTTP_RESPONSE_CODE");
-    if ( simCode )
-    {
-        _simResponseCode = osgEarth::as<long>(std::string(simCode), 404L);
-        OE_WARN << LC << "Simulating a network error with Response Code = " << _simResponseCode << std::endl;
-    }
-
-    // Check to HTTP disabling (for testing)
-    const char* disable = getenv("OSGEARTH_HTTP_DISABLE");
-    if (disable)
-    {
-        _simResponseCode = 503L; // SERVICE UNAVAILABLE
-    }
-
-    // Dumps out HTTP request/response info
-    if ( ::getenv("OSGEARTH_HTTP_DEBUG") )
-    {
-        s_HTTP_DEBUG = true;
-        OE_WARN << LC << "HTTP debugging enabled" << std::endl;
-    }
-
-    OE_DEBUG << LC << "HTTPClient setting userAgent=" << userAgent << std::endl;
-
-    curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, userAgent.c_str() );
-    curl_easy_setopt( _curl_handle, CURLOPT_WRITEFUNCTION, osgEarth::StreamObjectReadCallback );
-    curl_easy_setopt( _curl_handle, CURLOPT_HEADERFUNCTION, osgEarth::StreamObjectHeaderCallback );
-    curl_easy_setopt( _curl_handle, CURLOPT_FOLLOWLOCATION, (void*)1 );
-    curl_easy_setopt( _curl_handle, CURLOPT_MAXREDIRS, (void*)5 );
-    curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSFUNCTION, &CurlProgressCallback);
-    curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //0=enable.
-    curl_easy_setopt( _curl_handle, CURLOPT_FILETIME, true );
-
-    // Enable automatic CURL decompression of known types. An empty string will automatically add all supported encoding types that are built into curl.
-    // Note that you must have curl built against zlib to support gzip or deflate encoding.
-    curl_easy_setopt( _curl_handle, CURLOPT_ENCODING, "");
-
-    osg::ref_ptr< CurlConfigHandler > curlConfigHandler = getCurlConfigHandler();
-    if (curlConfigHandler.valid()) {
-        curlConfigHandler->onInitialize(_curl_handle);
-    }
-
-    long timeout = s_timeout;
-    const char* timeoutEnv = getenv("OSGEARTH_HTTP_TIMEOUT");
-    if (timeoutEnv)
-    {
-        timeout = osgEarth::as<long>(std::string(timeoutEnv), 0);
-    }
-    OE_DEBUG << LC << "Setting timeout to " << timeout << std::endl;
-    curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, timeout );
-    long connectTimeout = s_connectTimeout;
-    const char* connectTimeoutEnv = getenv("OSGEARTH_HTTP_CONNECTTIMEOUT");
-    if (connectTimeoutEnv)
-    {
-        connectTimeout = osgEarth::as<long>(std::string(connectTimeoutEnv), 0);
-    }
-    OE_DEBUG << LC << "Setting connect timeout to " << connectTimeout << std::endl;
-    curl_easy_setopt( _curl_handle, CURLOPT_CONNECTTIMEOUT, connectTimeout );
-
-    _initialized = true;
-}
-
-HTTPClient::~HTTPClient()
-{
-    if (_curl_handle) curl_easy_cleanup( _curl_handle );
-    _curl_handle = 0;
-}
-
-void
-HTTPClient::setProxySettings( const ProxySettings& proxySettings )
-{
-    s_proxySettings = proxySettings;
-}
-
-const std::string& HTTPClient::getUserAgent()
-{
-    return s_userAgent;
-}
-
-void  HTTPClient::setUserAgent(const std::string& userAgent)
-{
-    s_userAgent = userAgent;
-}
-
-long HTTPClient::getTimeout()
-{
-    return s_timeout;
-}
-
-void HTTPClient::setTimeout( long timeout )
-{
-    s_timeout = timeout;
-}
-
-long HTTPClient::getConnectTimeout()
-{
-    return s_connectTimeout;
-}
-
-void HTTPClient::setConnectTimeout( long timeout )
-{
-    s_connectTimeout = timeout;
-}
-URLRewriter* HTTPClient::getURLRewriter()
-{
-    return s_rewriter.get();
-}
-
-void HTTPClient::setURLRewriter( URLRewriter* rewriter )
-{
-    s_rewriter = rewriter;
-}
-
-CurlConfigHandler* HTTPClient::getCurlConfigHandler()
-{
-    return s_curlConfigHandler.get();
-}
-
-void HTTPClient::setCurlConfighandler(CurlConfigHandler* handler)
-{
-    s_curlConfigHandler = handler;
-}
-
-void
-HTTPClient::globalInit()
-{
-    curl_global_init(CURL_GLOBAL_ALL);
-}
-
-void
-HTTPClient::readOptions(const osgDB::Options* options, std::string& proxy_host, std::string& proxy_port) const
-{
-    // try to set proxy host/port by reading the CURL proxy options
-    if ( options )
-    {
-        std::istringstream iss( options->getOptionString() );
-        std::string opt;
-        while( iss >> opt )
-        {
-            int index = opt.find( "=" );
-            if( opt.substr( 0, index ) == "OSG_CURL_PROXY" )
-            {
-                proxy_host = opt.substr( index+1 );
-            }
-            else if ( opt.substr( 0, index ) == "OSG_CURL_PROXYPORT" )
-            {
-                proxy_port = opt.substr( index+1 );
-            }
-        }
-    }
-}
-
-bool
-HTTPClient::decodeMultipartStream(const std::string&   boundary,
-                                  HTTPResponse::Part*  input,
-                                  HTTPResponse::Parts& output) const
-{
-    std::string bstr = std::string("--") + boundary;
-    std::string line;
-    char tempbuf[256];
-
-    // first thing in the stream should be the boundary.
-    input->_stream.read( tempbuf, bstr.length() );
-    tempbuf[bstr.length()] = 0;
-    line = tempbuf;
-    if ( line != bstr )
-    {
-        OE_INFO << LC 
-            << "decodeMultipartStream: protocol violation; "
-            << "expecting boundary; instead got: \"" 
-            << line
-            << "\"" << std::endl;
-        return false;
-    }
-
-    for( bool done=false; !done; )
-    {
-        osg::ref_ptr<HTTPResponse::Part> next_part = new HTTPResponse::Part();
-
-        // first finish off the boundary.
-        std::getline( input->_stream, line );
-        if ( line == "--" )
-        {
-            done = true;
-        }
-        else
-        {
-            // read all headers. this ends with a blank line.
-            line = " ";
-            while( line.length() > 0 && !done )
-            {
-                std::getline( input->_stream, line );
-
-                // check for EOS:
-                if ( line == "--" )
-                {
-                    done = true;
-                }
-                else
-                {                    
-                    StringTokenizer tok(":");
-                    StringVector tized;
-                    tok.tokenize(line, tized);            
-                    if ( tized.size() >= 2 )
-                        next_part->_headers[tized[0]] = tized[1];                        
-                }
-            }
-        }
-
-        if ( !done )
-        {
-            // read data until we reach the boundary
-            unsigned int bstr_ptr = 0;
-            std::string temp;
-            //unsigned int c = 0;
-            while( bstr_ptr < bstr.length() )
-            {
-                char b;
-                input->_stream.read( &b, 1 );
-                if ( b == bstr[bstr_ptr] )
-                {
-                    bstr_ptr++;
-                }
-                else
-                {
-                    for( unsigned int i=0; i<bstr_ptr; i++ )
-                    {
-                        next_part->_stream << bstr[i];
-                    }
-                    next_part->_stream << b;
-                    next_part->_size += bstr_ptr + 1;
-                    bstr_ptr = 0;
-                }
-            }
-            output.push_back( next_part.get() );
-        }
-    }
-
-    return true;
-}
-
-HTTPResponse
-HTTPClient::get( const HTTPRequest&    request,
-                 const osgDB::Options* options,
-                 ProgressCallback*     progress)
-{
-    return getClient().doGet( request, options, progress );
-}
-
-HTTPResponse 
-HTTPClient::get( const std::string&    url,
-                 const osgDB::Options* options,
-                 ProgressCallback*     progress)
-{
-    return getClient().doGet( url, options, progress);
-}
-
-ReadResult
-HTTPClient::readImage(const HTTPRequest&    request,
-                      const osgDB::Options* options,
-                      ProgressCallback*     progress)
-{
-    return getClient().doReadImage( request, options, progress );
-}
-
-ReadResult
-HTTPClient::readNode(const HTTPRequest&    request,
-                     const osgDB::Options* options,
-                     ProgressCallback*     progress)
-{
-    return getClient().doReadNode( request, options, progress );
-}
-
-ReadResult
-HTTPClient::readObject(const HTTPRequest&    request,
-                       const osgDB::Options* options,
-                       ProgressCallback*     progress)
-{
-    return getClient().doReadObject( request, options, progress );
-}
-
-ReadResult
-HTTPClient::readString(const HTTPRequest&    request,
-                       const osgDB::Options* options,
-                       ProgressCallback*     progress)
-{
-    return getClient().doReadString( request, options, progress );
-}
-
-bool
-HTTPClient::download(const std::string& uri,
-                     const std::string& localPath)
-{
-    return getClient().doDownload( uri, localPath );
-}
-
-
-#ifdef OSGEARTH_USE_WININET_FOR_HTTP
-
-namespace
-{
-    std::string GetLastErrorAsString()
-    {
-        //Get the error message, if any.
-        DWORD errorMessageID = ::GetLastError();
-        if(errorMessageID == 0)
-            return std::string("Error Code 0"); //No error message has been recorded
-
-        LPSTR messageBuffer = nullptr;
-        size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                                     NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
-
-        std::string message(messageBuffer, size);
-
-        //Free the buffer.
-        LocalFree(messageBuffer);
-
-        message = Stringify() << "[Code " << errorMessageID << "] " << message;
-
-        return message;
-    }
-}
-
-HTTPResponse
-HTTPClient::doGet(const HTTPRequest&    request,
-                  const osgDB::Options* options, 
-                  ProgressCallback*     progress) const
-{
-    OE_START_TIMER(http_get);
-
-    std::string url = request.getURL();
-    // Rewrite the url if the url rewriter is available  
-    osg::ref_ptr< URLRewriter > rewriter = getURLRewriter();
-    if ( rewriter.valid() )
-    {
-        std::string oldURL = url;
-        url = rewriter->rewrite( oldURL );
-        OE_DEBUG << LC << "Rewrote URL " << oldURL << " to " << url << std::endl;
-    }
-
-    HINTERNET hInternet = InternetOpen(
-        getUserAgent().c_str(),
-        //"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko",
-        INTERNET_OPEN_TYPE_PRECONFIG, 
-        NULL,       // proxy
-        NULL,       // proxy bypass
-        0);         // flags
-
-    if( !hInternet )
-    {
-        OE_WARN << LC << "InternetOpen failed: " << GetLastErrorAsString() << std::endl;
-        return HTTPResponse(0);
-    }
-
-    // clears any session cookies..?
-    InternetSetOption( 0, INTERNET_OPTION_END_BROWSER_SESSION, NULL, 0 );
-    
-    // parse the URL:
-    URL_COMPONENTS urlcomp;
-    ZeroMemory(&urlcomp, sizeof(urlcomp));
-	urlcomp.dwStructSize = sizeof(urlcomp);
-	urlcomp.dwHostNameLength = 1;
-    urlcomp.dwUserNameLength = 1;
-    urlcomp.dwPasswordLength = 1;
-    urlcomp.dwUrlPathLength  = 1;
-
-    if ( !InternetCrackUrl(url.c_str(), 0, 0L, &urlcomp) )
-    {
-        OE_WARN << LC << "InternetCrackUrl failed for " << url << ": " << GetLastErrorAsString() << std::endl;
-        InternetCloseHandle( hInternet );
-        return HTTPResponse(0);
-    }
-    
-    WORD port =
-        urlcomp.nPort != 0 ? urlcomp.nPort :
-        urlcomp.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_DEFAULT_HTTPS_PORT :
-        INTERNET_DEFAULT_HTTP_PORT;
-
-    std::string hostName( urlcomp.lpszHostName );
-    int slash = hostName.find_first_of('/');
-    if ( slash != std::string::npos )
-        hostName = hostName.substr(0, slash);
-
-    OE_DEBUG
-        << "\n"
-        << "Host name = " << hostName << "\n"
-        << "Url path = " << urlcomp.lpszUrlPath << "\n"
-        << "Port = " << port << "\n";
-    
-    DWORD openFlags =
-        //INTERNET_FLAG_PRAGMA_NOCACHE |
-        INTERNET_FLAG_RELOAD |
-        INTERNET_FLAG_NO_CACHE_WRITE |
-        INTERNET_FLAG_KEEP_CONNECTION;
-
-    if ( urlcomp.nScheme == INTERNET_SCHEME_HTTPS)
-    {
-        openFlags |= INTERNET_FLAG_SECURE;
-    }
-
-    // InternetOpenUrl is a lot less code, but we have to use the InternetConnnect +
-    // HttpOpenRequest + HttpSendRequest approach in order to support system dialogs
-    // for PKI certificates and username/password queries.
-    
-    HINTERNET hConnection = InternetConnect(
-        hInternet,
-        hostName.c_str(),
-        port,
-        "", // username
-        "", // password
-        INTERNET_SERVICE_HTTP,
-        0,  // flags
-        INTERNET_NO_CALLBACK); // context
-
-    if ( !hConnection )
-    {
-        OE_WARN << LC << "InternetConnect failed for " << url << ": " << GetLastErrorAsString() << std::endl;
-        InternetCloseHandle( hInternet );
-        return HTTPResponse(0);
-    }
-
-    HINTERNET hRequest = HttpOpenRequest(
-        hConnection,            // handle from InternetConnect
-        "GET",                  // verb
-        urlcomp.lpszUrlPath,    // path
-        NULL,                   // HTTP version (NULL = default)
-        NULL,                   // Referrer
-        NULL,                   // Accept types
-        openFlags,              // flags
-        INTERNET_NO_CALLBACK);                  // context (user data)
-
-    if ( !hRequest )
-    {
-        OE_WARN << LC << "HttpOpenRequest failed for " << url << ": " << GetLastErrorAsString() << std::endl;
-        InternetCloseHandle( hConnection );
-        InternetCloseHandle( hInternet );
-        return HTTPResponse(0);
-    }
-
-    while( !HttpSendRequest(hRequest, NULL, 0, NULL, 0) )
-    {
-        DWORD errorNum = GetLastError();
-            
-        // Request for client cert; open system dialog.
-        if ( errorNum == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED )
-        {
-            OE_WARN << LC << "Server reports ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED!\n";
-
-            // Return ERROR_SUCCESS regardless of clicking on OK or Cancel
-            DWORD dialogResult = InternetErrorDlg(
-                GetDesktopWindow(), 
-                hRequest,
-                ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
-                FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, 
-                NULL );
-
-            if ( dialogResult != ERROR_SUCCESS )
-            {
-                OE_WARN << LC << "InternetErrorDlg failed to produce client cert " << url << ": " << GetLastErrorAsString() << std::endl;
-                InternetCloseHandle( hRequest );
-                InternetCloseHandle( hConnection );
-                InternetCloseHandle( hInternet );
-                return HTTPResponse(0);
-            }
-        }
-
-        else
-        {
-            OE_WARN << LC << "HttpSendRequest failed to open " << url << ": " << GetLastErrorAsString() << std::endl;
-            InternetCloseHandle( hRequest );
-            InternetCloseHandle( hConnection );
-            InternetCloseHandle( hInternet );
-            return HTTPResponse(0);
-        }
-    }
-
-    int statusCode = 0;
-    std::string contentType;
-
-    char  buffer[4096];
-    DWORD bufferLen = 4096;
-    DWORD index = 0;
-
-    if ( HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, buffer, &bufferLen, &index) )
-    {             
-        statusCode = as<int>( std::string(buffer, bufferLen), 0 );
-    }
-    
-    HTTPResponse response(statusCode);
-
-    bufferLen = 4096, index = 0;
-    if ( HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_TYPE, buffer, &bufferLen, &index) )
-    {
-        response._mimeType = std::string(buffer, bufferLen);
-    }
-
-    bufferLen = 4096, index = 0;
-    if ( HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED, buffer, &bufferLen, &index) )
-    {
-        response._lastModified = as<long>(std::string(buffer, bufferLen), 0L);
-    }
-
-    response._mimeType = contentType;
-
-    if ( statusCode == 200 )
-    {    
-        osg::ref_ptr<HTTPResponse::Part> part = new HTTPResponse::Part();
-
-        DWORD numBytesRead = 0;
-        while( InternetReadFile(hRequest, buffer, 4096, &numBytesRead) && numBytesRead )
-        {
-            part->_stream << std::string(buffer, numBytesRead);
-        }
-
-        response._parts.push_back( part.get() );
-    }
-
-    InternetCloseHandle( hRequest );
-    InternetCloseHandle( hConnection );
-    InternetCloseHandle( hInternet );
-
-    response._duration_s = OE_STOP_TIMER(http_get);
-
-    if ( progress )
-    {
-        progress->stats("http_get_time") += OE_GET_TIMER(http_get);
-        progress->stats("http_get_count") += 1;
-        if ( response._cancelled )
-            progress->stats("http_cancel_count") += 1;
-    }
-    
-    return response;
-}
-
-#else // OSGEARTH_USE_WININET_FOR_HTTP
-
-HTTPResponse
-HTTPClient::doGet(const HTTPRequest&    request,
-                  const osgDB::Options* options, 
-                  ProgressCallback*     progress) const
-{    
-    initialize();
-
-    OE_START_TIMER(http_get);
-    
-    std::string url = request.getURL();
-
-    const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ? 
-            options->getAuthenticationMap() :
-            osgDB::Registry::instance()->getAuthenticationMap();
-
-    std::string proxy_host;
-    std::string proxy_port = "8080";
-
-    std::string proxy_auth;
-
-    //TODO: don't do all this proxy setup on every GET. Just do it once per client, or only when 
-    // the proxy information changes.
-
-    //Try to get the proxy settings from the global settings
-    if (s_proxySettings.isSet())
-    {
-        proxy_host = s_proxySettings.get().hostName();
-        std::stringstream buf;
-        buf << s_proxySettings.get().port();
-        proxy_port = buf.str();
-
-        std::string proxy_username = s_proxySettings.get().userName();
-        std::string proxy_password = s_proxySettings.get().password();
-        if (!proxy_username.empty() && !proxy_password.empty())
-        {
-            proxy_auth = proxy_username + std::string(":") + proxy_password;
-        }
-    }
-
-    //Try to get the proxy settings from the local options that are passed in.
-    readOptions( options, proxy_host, proxy_port );
-
-    optional< ProxySettings > proxySettings;
-    ProxySettings::fromOptions( options, proxySettings );
-    if (proxySettings.isSet())
-    {       
-        proxy_host = proxySettings.get().hostName();
-        proxy_port = toString<int>(proxySettings.get().port());
-        OE_DEBUG << LC << "Read proxy settings from options " << proxy_host << " " << proxy_port << std::endl;
-    }
-
-    //Try to get the proxy settings from the environment variable
-    const char* proxyEnvAddress = getenv("OSG_CURL_PROXY");
-    if (proxyEnvAddress) //Env Proxy Settings
-    {
-        proxy_host = std::string(proxyEnvAddress);
-
-        const char* proxyEnvPort = getenv("OSG_CURL_PROXYPORT"); //Searching Proxy Port on Env
-        if (proxyEnvPort)
-        {
-            proxy_port = std::string( proxyEnvPort );
-        }
-    }
-
-    const char* proxyEnvAuth = getenv("OSGEARTH_CURL_PROXYAUTH");
-    if (proxyEnvAuth)
-    {
-        proxy_auth = std::string(proxyEnvAuth);
-    }
-
-    // Set up proxy server:
-    std::string proxy_addr;
-    if ( !proxy_host.empty() )
-    {
-        std::stringstream buf;
-        buf << proxy_host << ":" << proxy_port;
-        std::string bufStr;
-        bufStr = buf.str();
-        proxy_addr = bufStr;
-    
-        if ( s_HTTP_DEBUG )
-        {
-            OE_NOTICE << LC << "Using proxy: " << proxy_addr << std::endl;
-        }
-
-        //curl_easy_setopt( _curl_handle, CURLOPT_HTTPPROXYTUNNEL, 1 ); 
-        curl_easy_setopt( _curl_handle, CURLOPT_PROXY, proxy_addr.c_str() );
-
-        //Setup the proxy authentication if setup
-        if (!proxy_auth.empty())
-        {
-            if ( s_HTTP_DEBUG )
-            {
-                OE_NOTICE << LC << "Using proxy authentication " << proxy_auth << std::endl;
-            }
-
-            curl_easy_setopt( _curl_handle, CURLOPT_PROXYUSERPWD, proxy_auth.c_str());
-        }
-    }
-    else
-    {
-        OE_DEBUG << LC << "Removing proxy settings" << std::endl;
-        curl_easy_setopt( _curl_handle, CURLOPT_PROXY, 0 );
-    }
-
-    // Rewrite the url if the url rewriter is available  
-    osg::ref_ptr< URLRewriter > rewriter = getURLRewriter();
-    if ( rewriter.valid() )
-    {
-        std::string oldURL = url;
-        url = rewriter->rewrite( oldURL );
-        OE_DEBUG << LC << "Rewrote URL " << oldURL << " to " << url << std::endl;
-    }
-
-    const osgDB::AuthenticationDetails* details = authenticationMap ?
-        authenticationMap->getAuthenticationDetails( url ) :
-        0;
-
-    if (details)
-    {
-        const std::string colon(":");
-        std::string password(details->username + colon + details->password);
-        curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, password.c_str());
-        const_cast<HTTPClient*>(this)->_previousPassword = password;
-
-        // use for https.
-        // curl_easy_setopt(_curl, CURLOPT_KEYPASSWD, password.c_str());
-
-#if LIBCURL_VERSION_NUM >= 0x070a07
-        if (details->httpAuthentication != _previousHttpAuthentication)
-        { 
-            curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, details->httpAuthentication); 
-            const_cast<HTTPClient*>(this)->_previousHttpAuthentication = details->httpAuthentication;
-        }
-#endif
-    }
-    else
-    {
-        if (!_previousPassword.empty())
-        {
-            curl_easy_setopt(_curl_handle, CURLOPT_USERPWD, 0);
-            const_cast<HTTPClient*>(this)->_previousPassword.clear();
-        }
-
-#if LIBCURL_VERSION_NUM >= 0x070a07
-        // need to reset if previously set.
-        if (_previousHttpAuthentication!=0)
-        {
-            curl_easy_setopt(_curl_handle, CURLOPT_HTTPAUTH, 0); 
-            const_cast<HTTPClient*>(this)->_previousHttpAuthentication = 0;
-        }
-#endif
-    }  
-
-
-    // Set any headers
-    struct curl_slist *headers=NULL;
-    if (!request.getHeaders().empty())
-    {
-        for (HTTPRequest::Parameters::const_iterator itr = request.getHeaders().begin(); itr != request.getHeaders().end(); ++itr)
-        {
-            std::stringstream buf;
-            buf << itr->first << ": " << itr->second;
-            headers = curl_slist_append(headers, buf.str().c_str());
-        }
-    }
-
-    // Disable the default Pragma: no-cache that curl adds by default.
-    headers = curl_slist_append(headers, "Pragma: ");
-    curl_easy_setopt(_curl_handle, CURLOPT_HTTPHEADER, headers); 
-    
-    osg::ref_ptr<HTTPResponse::Part> part = new HTTPResponse::Part();
-    StreamObject sp( &part->_stream );
-
-    //Take a temporary ref to the callback (why? dangerous.)
-    //osg::ref_ptr<ProgressCallback> progressCallback = callback;
-    curl_easy_setopt( _curl_handle, CURLOPT_URL, url.c_str() );
-    if (progress)
-    {
-        curl_easy_setopt(_curl_handle, CURLOPT_PROGRESSDATA, progress);
-    }
-
-    CURLcode res;
-    long response_code = 0L;
-
-    OE_START_TIMER(get_duration);
-
-    if ( _simResponseCode < 0 )
-    {
-        char errorBuf[CURL_ERROR_SIZE];
-        errorBuf[0] = 0;
-        curl_easy_setopt( _curl_handle, CURLOPT_ERRORBUFFER, (void*)errorBuf );
-        curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)&sp);
-        curl_easy_setopt( _curl_handle, CURLOPT_HEADERDATA, (void*)&sp);
-
-        //Disable peer certificate verification to allow us to access in https servers where the peer certificate cannot be verified.
-        curl_easy_setopt( _curl_handle, CURLOPT_SSL_VERIFYPEER, (void*)0 );
-        
-        osg::ref_ptr< CurlConfigHandler > curlConfigHandler = getCurlConfigHandler();
-        if (curlConfigHandler.valid()) {
-            curlConfigHandler->onGet(_curl_handle);
-        }
-
-        res = curl_easy_perform(_curl_handle);
-        curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, (void*)0 );
-        curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSDATA, (void*)0);
-
-        if (!proxy_addr.empty())
-        {
-            long connect_code = 0L;
-            CURLcode r = curl_easy_getinfo(_curl_handle, CURLINFO_HTTP_CONNECTCODE, &connect_code);
-            if ( r != CURLE_OK )
-            {
-                OE_WARN << LC << "Proxy connect error: " << curl_easy_strerror(r) << std::endl;
-                return HTTPResponse(0);
-            }
-        }
-
-        curl_easy_getinfo( _curl_handle, CURLINFO_RESPONSE_CODE, &response_code );        
-    }
-    else
-    {
-        // simulate failure with a custom response code
-        response_code = _simResponseCode;
-        res = response_code == 408 ? CURLE_OPERATION_TIMEDOUT : CURLE_COULDNT_CONNECT;
-    }
-
-    HTTPResponse response( response_code );    
-    
-    // read the response content type:
-    char* content_type_cp;
-
-    curl_easy_getinfo( _curl_handle, CURLINFO_CONTENT_TYPE, &content_type_cp );    
-
-    if ( content_type_cp != NULL )
-    {
-        response._mimeType = content_type_cp;    
-    } 
-
-    // read the file time:
-    response._lastModified = getCurlFileTime( _curl_handle );
-
-    // upon success, parse the data:
-    if ( res != CURLE_ABORTED_BY_CALLBACK && res != CURLE_OPERATION_TIMEDOUT )
-    {        
-        // check for multipart content
-        if (response._mimeType.length() > 9 && 
-            ::strstr( response._mimeType.c_str(), "multipart" ) == response._mimeType.c_str() )
-        {
-            OE_DEBUG << LC << "detected multipart data; decoding..." << std::endl;
-
-            //TODO: parse out the "wcs" -- this is WCS-specific
-            if ( !decodeMultipartStream( "wcs", part.get(), response._parts ) )
-            {
-                // error decoding an invalid multipart stream.
-                // should we do anything, or just leave the response empty?
-            }
-        }
-        else
-        {            
-            for (Headers::iterator itr = sp._headers.begin(); itr != sp._headers.end(); ++itr)
-            {                
-                part->_headers[itr->first] = itr->second;                
-            }
-
-            // Write the headers to the metadata
-            response._parts.push_back( part.get() );
-        }
-    }
-    else  /*if (res == CURLE_ABORTED_BY_CALLBACK || res == CURLE_OPERATION_TIMEDOUT) */
-    {        
-        //If we were aborted by a callback, then it was cancelled by a user
-        response._cancelled = true;
-    }
-
-    response._duration_s = OE_STOP_TIMER(get_duration);
-
-    if ( progress )
-    {
-        progress->stats()["http_get_time"] += OE_STOP_TIMER(http_get);
-        progress->stats()["http_get_count"] += 1;
-        if ( response._cancelled )
-            progress->stats()["http_cancel_count"] += 1;
-    }
-
-    if ( s_HTTP_DEBUG )
-    {
-        TimeStamp filetime = getCurlFileTime(_curl_handle);
-
-        OE_NOTICE << LC 
-            << "GET(" << response_code << ", " << response._mimeType << ") : \"" 
-            << url << "\" (" << DateTime(filetime).asRFC1123() << ") t="
-            << std::setprecision(4) << response.getDuration() << "s" << std::endl;
-
-        {
-            Threading::ScopedMutexLock lock(s_HTTP_DEBUG_mutex);
-            s_HTTP_DEBUG_request_count++;
-            s_HTTP_DEBUG_total_duration += response.getDuration();
-
-            if ( s_HTTP_DEBUG_request_count % 60 == 0 )
-            {
-                OE_NOTICE << LC << "Average duration = " << s_HTTP_DEBUG_total_duration/(double)s_HTTP_DEBUG_request_count
-                    << std::endl;
-            }
-        }
-
-#if 0
-        // time details - almost 100% of the time is spent in
-        // STARTTRANSFER, which is the time until the first byte is received.
-        double td[7];
-
-        curl_easy_getinfo(_curl_handle, CURLINFO_TOTAL_TIME,         &td[0]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_NAMELOOKUP_TIME,    &td[1]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_CONNECT_TIME,       &td[2]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_APPCONNECT_TIME,    &td[3]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_PRETRANSFER_TIME,   &td[4]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_STARTTRANSFER_TIME, &td[5]);
-        curl_easy_getinfo(_curl_handle, CURLINFO_REDIRECT_TIME,      &td[6]);
-
-        for(int i=0; i<7; ++i)
-        {
-            OE_NOTICE << LC
-                << std::setprecision(4)
-                << "TIMES: total=" <<td[0]
-                << ", lookup=" <<td[1]<<" ("<<(int)((td[1]/td[0])*100)<<"%)"
-                << ", connect=" <<td[2]<<" ("<<(int)((td[2]/td[0])*100)<<"%)"
-                << ", appconn=" <<td[3]<<" ("<<(int)((td[3]/td[0])*100)<<"%)"
-                << ", prexfer=" <<td[4]<<" ("<<(int)((td[4]/td[0])*100)<<"%)"
-                << ", startxfer=" <<td[5]<<" ("<<(int)((td[5]/td[0])*100)<<"%)"
-                << ", redir=" <<td[6]<<" ("<<(int)((td[6]/td[0])*100)<<"%)"
-                << std::endl;
-        }
-#endif
-
-        // Free the headers
-        if (headers)
-        {
-            curl_slist_free_all(headers);
-        }
-    }
-
-    return response;
-}
-
-#endif // USE_WININET
-
-bool
-HTTPClient::doDownload(const std::string& url, const std::string& filename)
-{
-    initialize();
-
-    // download the data
-    HTTPResponse response = this->doGet( HTTPRequest(url) );
-
-    if ( response.isOK() )
-    {
-        unsigned int part_num = response.getNumParts() > 1? 1 : 0;
-        std::istream& input_stream = response.getPartStream( part_num );
-
-        std::ofstream fout;
-        fout.open(filename.c_str(), std::ios::out | std::ios::binary);
-
-        input_stream.seekg (0, std::ios::end);
-        int length = input_stream.tellg();
-        input_stream.seekg (0, std::ios::beg);
-
-        char *buffer = new char[length];
-        input_stream.read(buffer, length);
-        fout.write(buffer, length);
-        delete[] buffer;
-        fout.close();
-        return true;
-    }
-    else
-    {
-        OE_WARN << LC << "Error downloading file " << filename
-            << " (" << response.getCode() << ")" << std::endl;
-        return false;
-    } 
-}
-
-namespace
-{
-    osgDB::ReaderWriter*
-    getReader( const std::string& url, const HTTPResponse& response )
-    {        
-        osgDB::ReaderWriter* reader = 0L;
-
-        // try extension first:
-        std::string ext = osgDB::getFileExtension( url );
-        if ( !ext.empty() )
-        {
-            reader = osgDB::Registry::instance()->getReaderWriterForExtension( ext );
-        }
-
-        if ( !reader )
-        {
-            // try to look up a reader by mime-type first:
-            std::string mimeType = response.getMimeType();
-            if ( !mimeType.empty() )
-            {
-                reader = osgDB::Registry::instance()->getReaderWriterForMimeType(mimeType);
-            }
-        }
-
-        if ( !reader && s_HTTP_DEBUG )
-        {
-            OE_WARN << LC << "Cannot find an OSG plugin to read response data (ext="
-                << ext << "; mime-type=" << response.getMimeType()
-                << ")" << std::endl;
-
-            if ( endsWith(response.getMimeType(), "xml", false) )
-            {
-                OE_WARN << LC << "Content:\n" << response.getPartAsString(0) << "\n";
-            }
-        }
-
-        return reader;
-    }
-}
-
-ReadResult
-HTTPClient::doReadImage(const HTTPRequest&    request,
-                        const osgDB::Options* options,
-                        ProgressCallback*     callback)
-{
-    initialize();
-
-    ReadResult result;
-
-    HTTPResponse response = this->doGet(request, options, callback);
-
-    if (response.isOK())
-    {
-        osgDB::ReaderWriter* reader = getReader(request.getURL(), response);
-        if (!reader)
-        {            
-            result = ReadResult(ReadResult::RESULT_NO_READER);
-        }
-
-        else 
-        {
-            osgDB::ReaderWriter::ReadResult rr = reader->readImage(response.getPartStream(0), options);
-            if ( rr.validImage() )
-            {
-                result = ReadResult(rr.takeImage());
-            }
-            else 
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_WARN << LC << reader->className() 
-                        << " failed to read image from " << request.getURL() 
-                        << "; message = " << rr.message()
-                        <<  std::endl;
-                }
-                result = ReadResult(ReadResult::RESULT_READER_ERROR);
-                result.setErrorDetail( rr.message() );
-            }
-        }
-        
-        // last-modified (file time)
-        result.setLastModifiedTime( response._lastModified ); //getCurlFileTime(_curl_handle) );
-        
-        // Time of query
-        result.setDuration( response.getDuration() );
-    }
-    else
-    {
-        result = ReadResult(
-            response.isCancelled()                           ? ReadResult::RESULT_CANCELED :
-            response.getCode() == HTTPResponse::NOT_FOUND    ? ReadResult::RESULT_NOT_FOUND :
-            response.getCode() == HTTPResponse::SERVER_ERROR ? ReadResult::RESULT_SERVER_ERROR :
-            response.getCode() == HTTPResponse::NOT_MODIFIED ? ReadResult::RESULT_NOT_MODIFIED :
-                                                               ReadResult::RESULT_UNKNOWN_ERROR );
-
-        //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
-        if (HTTPClient::isRecoverable( result.code() ) )
-        {            
-            if (callback)
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_NOTICE << LC << "Error in HTTPClient for " << request.getURL() << " but it's recoverable" << std::endl;
-                }
-                callback->setNeedsRetry( true );
-            }
-        }        
-    }
-
-    // encode headers
-    result.setMetadata( response.getHeadersAsConfig() );
-
-    // set the source name
-    if ( result.getImage() )
-        result.getImage()->setName( request.getURL() );
-
-    return result;
-}
-
-ReadResult
-HTTPClient::doReadNode(const HTTPRequest&    request,
-                       const osgDB::Options* options,
-                       ProgressCallback*     callback)
-{
-    initialize();
-
-    ReadResult result;
-
-    HTTPResponse response = this->doGet(request, options, callback);
-
-    if (response.isOK())
-    {
-        osgDB::ReaderWriter* reader = getReader(request.getURL(), response);
-        if (!reader)
-        {
-            result = ReadResult(ReadResult::RESULT_NO_READER);
-        }
-
-        else 
-        {
-            osgDB::ReaderWriter::ReadResult rr = reader->readNode(response.getPartStream(0), options);
-            if ( rr.validNode() )
-            {
-                result = ReadResult(rr.takeNode());
-            }
-            else 
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_WARN << LC << reader->className() 
-                        << " failed to read node from " << request.getURL() 
-                        << "; message = " << rr.message()
-                        <<  std::endl;
-                }
-                result = ReadResult(ReadResult::RESULT_READER_ERROR);
-                result.setErrorDetail( rr.message() );
-            }
-        }
-        
-        // last-modified (file time)
-        result.setLastModifiedTime( response._lastModified ); //getCurlFileTime(_curl_handle) );
-    }
-    else
-    {
-        result = ReadResult(
-            response.isCancelled()                           ? ReadResult::RESULT_CANCELED :
-            response.getCode() == HTTPResponse::NOT_FOUND    ? ReadResult::RESULT_NOT_FOUND :
-            response.getCode() == HTTPResponse::SERVER_ERROR ? ReadResult::RESULT_SERVER_ERROR :
-            response.getCode() == HTTPResponse::NOT_MODIFIED ? ReadResult::RESULT_NOT_MODIFIED :
-                                                               ReadResult::RESULT_UNKNOWN_ERROR );
-
-        //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
-        if (HTTPClient::isRecoverable( result.code() ) )
-        {
-            if (callback)
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_NOTICE << LC << "Error in HTTPClient for " << request.getURL() << " but it's recoverable" << std::endl;
-                }
-                callback->setNeedsRetry( true );
-            }
-        }
-    }
-
-    // encode headers
-    result.setMetadata( response.getHeadersAsConfig() );
-
-    return result;
-}
-
-ReadResult
-HTTPClient::doReadObject(const HTTPRequest&    request,
-                         const osgDB::Options* options,
-                         ProgressCallback*     callback)
-{
-    initialize();
-
-    ReadResult result;
-
-    HTTPResponse response = this->doGet(request, options, callback);
-
-    if (response.isOK())
-    {
-        osgDB::ReaderWriter* reader = getReader(request.getURL(), response);
-        if (!reader)
-        {
-            result = ReadResult(ReadResult::RESULT_NO_READER);
-        }
-
-        else 
-        {
-            osgDB::ReaderWriter::ReadResult rr = reader->readObject(response.getPartStream(0), options);
-            if ( rr.validObject() )
-            {
-                result = ReadResult(rr.takeObject());
-            }
-            else 
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_WARN << LC << reader->className() 
-                        << " failed to read object from " << request.getURL() 
-                        << "; message = " << rr.message()
-                        <<  std::endl;
-                }
-                result = ReadResult(ReadResult::RESULT_READER_ERROR);
-                result.setErrorDetail( rr.message() );
-            }
-        }
-        
-        // last-modified (file time)
-        result.setLastModifiedTime( response._lastModified ); //getCurlFileTime(_curl_handle) );
-    }
-    else
-    {
-        result = ReadResult(
-            response.isCancelled() ? ReadResult::RESULT_CANCELED :
-            response.getCode() == HTTPResponse::NOT_FOUND ? ReadResult::RESULT_NOT_FOUND :
-            response.getCode() == HTTPResponse::SERVER_ERROR ? ReadResult::RESULT_SERVER_ERROR :
-            response.getCode() == HTTPResponse::NOT_MODIFIED ? ReadResult::RESULT_NOT_MODIFIED :
-            ReadResult::RESULT_UNKNOWN_ERROR );
-
-        //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
-        if (HTTPClient::isRecoverable( result.code() ) )
-        {
-            if (callback)
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_NOTICE << LC << "Error in HTTPClient for " << request.getURL() << " but it's recoverable" << std::endl;
-                }
-                callback->setNeedsRetry( true );
-            }
-        }
-    }
-
-    result.setMetadata( response.getHeadersAsConfig() );
-
-    return result;
-}
-
-
-ReadResult
-HTTPClient::doReadString(const HTTPRequest&    request,
-                         const osgDB::Options* options,
-                         ProgressCallback*     callback )
-{
-    initialize();
-
-    ReadResult result;
-
-    HTTPResponse response = this->doGet( request, options, callback );
-    if ( response.isOK() )
-    {
-        result = ReadResult( new StringObject(response.getPartAsString(0)) );
-    }
-
-    else if ( response.getCode() >= 400 && response.getCode() < 500 && response.getCode() != 404 )
-    {
-        // for request errors, return an error result with the part data intact
-        // so the user can parse it as needed. We only do this for readString.
-        result = ReadResult( 
-            ReadResult::RESULT_SERVER_ERROR,
-            new StringObject(response.getPartAsString(0)) );
-    }
-
-    else
-    {
-        result = ReadResult(
-            response.isCancelled() ?                           ReadResult::RESULT_CANCELED :
-            response.getCode() == HTTPResponse::NOT_FOUND    ? ReadResult::RESULT_NOT_FOUND :
-            response.getCode() == HTTPResponse::SERVER_ERROR ? ReadResult::RESULT_SERVER_ERROR :
-            response.getCode() == HTTPResponse::NOT_MODIFIED ? ReadResult::RESULT_NOT_MODIFIED :
-                                                               ReadResult::RESULT_UNKNOWN_ERROR );
-
-        //If we have an error but it's recoverable, like a server error or timeout then set the callback to retry.
-        if (HTTPClient::isRecoverable( result.code() ) )
-        {            
-            if (callback)
-            {
-                if ( s_HTTP_DEBUG )
-                {
-                    OE_NOTICE << LC << "Error in HTTPClient for " << request.getURL() << " but it's recoverable" << std::endl;
-                }
-                callback->setNeedsRetry( true );
-            }
-        }
-    }
-
-    // encode headers
-    result.setMetadata( response.getHeadersAsConfig() );
-
-    // last-modified (file time)
-    result.setLastModifiedTime( response._lastModified ); //getCurlFileTime(_curl_handle) );
-
-    return result;
-}
+/* -*-c++-*- */
+/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
+ * Copyright 2016 Pelican Mapping
+ * http://osgearth.org
+ *
+ * osgEarth is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+#include <osgEarth/HTTPClient>
+#include <osgEarth/Registry>
+#include <osgEarth/Version>
+#include <osgEarth/Progress>
+#include <osgEarth/StringUtils>
+#include <osgEarth/Metrics>
+#include <osgDB/ReadFile>
+#include <osgDB/Registry>
+#include <osgDB/FileNameUtils>
+#include <osg/Notify>
+#include <osg/Timer>
+#include <string.h>
+#include <sstream>
+#include <fstream>
+#include <iterator>
+#include <iostream>
+#include <algorithm>
+#include <curl/curl.h>
+
+// Whether to use WinInet instead of cURL - CMAKE option
+#ifdef OSGEARTH_USE_WININET_FOR_HTTP
+#include <WinInet.h>
+#pragma comment(lib, "wininet.lib")
+#endif
+
+#define LC "[HTTPClient] "
+
+//#define OE_TEST OE_NOTICE
+#define OE_TEST OE_NULL
+
+using namespace osgEarth;
+
+//----------------------------------------------------------------------------
+
+ProxySettings::ProxySettings( const Config& conf )
+{
+    mergeConfig( conf );
+}
+
+ProxySettings::ProxySettings( const std::string& host, int port ) :
+_hostName(host),
+_port(port)
+{
+    //nop
+}
+
+void
+ProxySettings::mergeConfig( const Config& conf )
+{
+    _hostName = conf.value<std::string>( "host", "" );
+    _port = conf.value<int>( "port", 8080 );
+    _userName = conf.value<std::string>( "username", "" );
+    _password = conf.value<std::string>( "password", "" );
+}
+
+Config
+ProxySettings::getConfig() const
+{
+    Config conf( "proxy" );
+    conf.add( "host", _hostName );
+    conf.add( "port", toString(_port) );
+    conf.add( "username", _userName);
+    conf.add( "password", _password);
+
+    return conf;
+}
+
+bool
+ProxySettings::fromOptions( const osgDB::Options* dbOptions, optional<ProxySettings>& out )
+{
+    if ( dbOptions )
+    {
+        std::string jsonString = dbOptions->getPluginStringData( "osgEarth::ProxySettings" );
+        if ( !jsonString.empty() )
+        {
+            Config conf;
+            conf.fromJSON( jsonString );
+            out = ProxySettings( conf );
+            return true;
+        }
+    }
+    return false;
+}
+
+void
+ProxySettings::apply( osgDB::Options* dbOptions ) const
+{
+    if ( dbOptions )
+    {
+        Config conf = getConfig();
+        dbOptions->setPluginStringData( "osgEarth::ProxySettings", conf.toJSON() );
+    }
+}
+
+/****************************************************************************/
+
+namespace osgEarth
+{
+    struct StreamObject
+    {
+        StreamObject(std::ostream* stream) : _stream(stream) { }
+
+        void write(const char* ptr, size_t realsize)
+        {
+            if (_stream) _stream->write(ptr, realsize);
+        }
+
+        void writeHeader(const char* ptr, size_t realsize)
+        {
+            std::string header(ptr);
+            StringTokenizer tok(":");
+            StringVector tized;
+            tok.tokenize(header, tized);
+            if ( tized.size() >= 2 )
+                _headers[tized[0]] = tized[1];
+        }
+
+        std::ostream* _stream;
+        Headers _headers;
+        std::string     _resultMimeType;
+    };
+
+    static size_t
+    StreamObjectReadCallback(void* ptr, size_t size, size_t nmemb, void* data)
+    {
+        size_t realsize = size* nmemb;
+        StreamObject* sp = (StreamObject*)data;
+        sp->write((const char*)ptr, realsize);
+        return realsize;
+    }
+
+    static size_t
+    StreamObjectHeaderCallback(void* ptr, size_t size, size_t nmemb, void* data)
+    {
+        size_t realsize = size* nmemb;
+        StreamObject* sp = (StreamObject*)data;
+        sp->writeHeader((const char*)ptr, realsize);
+        return realsize;
+    }
+
+    TimeStamp
+    getCurlFileTime(void* curl)
+    {
+        long filetime;
+        if (CURLE_OK != curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime))
+            return TimeStamp(0);
+        else if (filetime < 0)
+            return TimeStamp(0);
+        else
+            return TimeStamp(filetime);
+    }
+}
+
+static int CurlProgressCallback(void *clientp,double dltotal,double dlnow,double ultotal,double ulnow)
+{
+    ProgressCallback* callback = (ProgressCallback*)clientp;
+    bool cancelled = false;
+    if (callback)
+    {
+        cancelled = callback->isCanceled() || callback->reportProgress(dlnow, dltotal);
+    }
+    return cancelled;
+}
+
+/****************************************************************************/
+
+HTTPRequest::HTTPRequest( const std::string& url )
+: _url( url )
+{
+    //NOP
+}
+
+HTTPRequest::HTTPRequest( const HTTPRequest& rhs ) :
+_parameters( rhs._parameters ),
+_headers(rhs._headers),
+_url( rhs._url )
+{
+    //nop
+}
+
+void
+HTTPRequest::addParameter( const std::string& name, const std::string& value )
+{
+    _parameters[name] = value;
+}
+
+void
+HTTPRequest::addParameter( const std::string& name, int value )
+{
+    std::stringstream buf;
+    buf << value;
+     std::string bufStr;
+    bufStr = buf.str();
+    _parameters[name] = bufStr;
+}
+
+void
+HTTPRequest::addParameter( const std::string& name, double value )
+{
+    std::stringstream buf;
+    buf << value;
+     std::string bufStr;
+    bufStr = buf.str();
+    _parameters[name] = bufStr;
+}
+
+const HTTPRequest::Parameters&
+HTTPRequest::getParameters() const
+{
+    return _parameters;
+}
+
+void
+HTTPRequest::addHeader( const std::string& name, const std::string& value )
+{
+    _headers[name] = value;
+}
+
+const Headers&
+HTTPRequest::getHeaders() const
+{
+    return _headers;
+}
+
+void HTTPRequest::setLastModified( const DateTime &lastModified)
+{
+    addHeader("If-Modified-Since", lastModified.asRFC1123());
+}
+
+
+std::string
+HTTPRequest::getURL() const
+{
+    if ( _parameters.size() == 0 )
+    {
+        return _url;
+    }
+    else
+    {
+        std::stringstream buf;
+        buf << _url;
+        for( Parameters::const_iterator i = _parameters.begin(); i != _parameters.end(); i++ )
+        {
+            buf << ( i == _parameters.begin() && _url.find( "?" ) == std::string::npos? "?" : "&" );
+            buf << i->first << "=" << i->second;
+        }
+         std::string bufStr;
+         bufStr = buf.str();
+        return bufStr;
+    }
+}
+
+/****************************************************************************/
+
+HTTPResponse::HTTPResponse( long _code ) :
+_response_code( _code ),
+_cancelled(false),
+_duration_s(0.0),
+_lastModified(0u)
+{
+    _parts.reserve(1);
+}
+
+HTTPResponse::HTTPResponse( const HTTPResponse& rhs ) :
+_response_code( rhs._response_code ),
+_parts( rhs._parts ),
+_mimeType( rhs._mimeType ),
+_cancelled( rhs._cancelled ),
+_duration_s(0.0),
+_lastModified(0u)
+{
+    //nop
+}
+
+unsigned
+HTTPResponse::getCode() const {
+    return _response_code;
+}
+
+bool
+HTTPResponse::isOK() const {
+    return _response_code == 200L && !isCancelled();
+}
+
+bool
+HTTPResponse::isCancelled() const {
+    return _cancelled;
+}
+
+unsigned int
+HTTPResponse::getNumParts() const {
+    return _parts.size();
+}
+
+unsigned int
+HTTPResponse::getPartSize( unsigned int n ) const {
+    return _parts[n]->_size;
+}
+
+const std::string&
+HTTPResponse::getPartHeader( unsigned int n, const std::string& name ) const {
+    return _parts[n]->_headers[name];
+}
+
+std::istream&
+HTTPResponse::getPartStream( unsigned int n ) const {
+    return _parts[n]->_stream;
+}
+
+std::string
+HTTPResponse::getPartAsString( unsigned int n ) const {
+    std::string streamStr;
+    streamStr = _parts[n]->_stream.str();
+    return streamStr;
+}
+
+const std::string&
+HTTPResponse::getMimeType() const {
+    return _mimeType;
+}
+
+Config
+HTTPResponse::getHeadersAsConfig() const
+{
+    Config conf;
+    if ( _parts.size() > 0 )
+    {
+        for( Headers::const_iterator i = _parts[0]->_headers.begin(); i != _parts[0]->_headers.end(); ++i )
+        {
+            conf.set(i->first, i->second);
+        }
+    }
+    return conf;
+}
+
+/****************************************************************************/
+
+#define QUOTE_(X) #X
+#define QUOTE(X) QUOTE_(X)
+#define USER_AGENT "osgearth" QUOTE(OSGEARTH_MAJOR_VERSION) "." QUOTE(OSGEARTH_MINOR_VERSION)
+
+
+namespace
+{
+    // TODO: consider moving this stuff into the osgEarth::Registry;
+    // don't like it here in the global scope
+    // per-thread client map (must be global scope)
+    static PerThread<HTTPClient>       s_clientPerThread;
+
+    static optional<ProxySettings>     s_proxySettings;
+
+    static std::string                 s_userAgent = USER_AGENT;
+
+    static long                        s_timeout = 0;
+    static long                        s_connectTimeout = 0;
+
+    // HTTP debugging.
+    static bool                        s_HTTP_DEBUG = false;
+    static Threading::Mutex            s_HTTP_DEBUG_mutex;
+    static int                         s_HTTP_DEBUG_request_count;
+    static double                      s_HTTP_DEBUG_total_duration;
+
+    static osg::ref_ptr< URLRewriter > s_rewriter;
+
+    static osg::ref_ptr< CurlConfigHandler > s_curlConfigHandler;
+}
+
+HTTPClient&
+HTTPClient::getClient()
+{
+    return s_clientPerThread.get();
+}
+
+HTTPClient::HTTPClient() :
+_initialized    ( false ),
+_curl_handle    ( 0L ),
+_simResponseCode( -1L ),
+_previousHttpAuthentication(0L)
+{
+    //nop
+    //do no CURL calls here.
+}
+
+void
+HTTPClient::initialize() const
+{
+    if ( !_initialized )
+    {
+        const_cast<HTTPClient*>(this)->initializeImpl();
+    }
+}
+
+void
+HTTPClient::initializeImpl()
+{
+    _previousHttpAuthentication = 0;
+    _curl_handle = curl_easy_init();
+
+    //Get the user agent
+    std::string userAgent = s_userAgent;
+    const char* userAgentEnv = getenv("OSGEARTH_USERAGENT");
+    if (userAgentEnv)
+    {
+        userAgent = std::string(userAgentEnv);
+    }
+
+    //Check for a response-code simulation (for testing)
+    const char* simCode = getenv("OSGEARTH_SIMULATE_HTTP_RESPONSE_CODE");
+    if ( simCode )
+    {
+        _simResponseCode = osgEarth::as<long>(std::string(simCode), 404L);
+        OE_WARN << LC << "Simulating a network error with Response Code = " << _simResponseCode << std::endl;
+    }
+
+    // Check to HTTP disabling (for testing)
+    const char* disable = getenv("OSGEARTH_HTTP_DISABLE");
+    if (disable)
+    {
+        _simResponseCode = 503L; // SERVICE UNAVAILABLE
+        OE_WARN << LC << "HTTP traffic disabled" << std::endl;
+    }
+
+    // Dumps out HTTP request/response info
+    if ( ::getenv("OSGEARTH_HTTP_DEBUG") )
+    {
+        s_HTTP_DEBUG = true;
+        OE_WARN << LC << "HTTP debugging enabled" << std::endl;
+    }
+
+    OE_DEBUG << LC << "HTTPClient setting userAgent=" << userAgent << std::endl;
+
+    curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, userAgent.c_str() );
+    curl_easy_setopt( _curl_handle, CURLOPT_WRITEFUNCTION, osgEarth::StreamObjectReadCallback );
+    curl_easy_setopt( _curl_handle, CURLOPT_HEADERFUNCTION, osgEarth::StreamObjectHeaderCallback );
+    curl_easy_setopt( _curl_handle, CURLOPT_FOLLOWLOCATION, (void*)1 );
+    curl_easy_setopt( _curl_handle, CURLOPT_MAXREDIRS, (void*)5 );
+    curl_easy_setopt( _curl_handle, CURLOPT_PROGRESSFUNCTION, &CurlProgressCallback);
+    curl_easy_setopt( _curl_handle, CURLOPT_NOPROGRESS, (void*)0 ); //0=enable.
+    curl_easy_setopt( _curl_handle, CURLOPT_FILETIME, true );
+
+    // Enable automatic CURL decompression of known types. An empty string will automatically add all supported encoding types that are built into curl.
+    // Note that you must have curl built against zlib to support gzip or deflate encoding.
+    curl_easy_setopt( _curl_handle, CURLOPT_ENCODING, "");
+
+    osg::ref_ptr< CurlConfigHandler > curlConfigHandler = getCurlConfigHandler();
+    if (curlConfigHandler.valid()) {
+        curlConfigHandler->onInitialize(_curl_handle);
+    }
+
+    long timeout = s_timeout;
+    const char* timeoutEnv = getenv("OSGEARTH_HTTP_TIMEOUT");
+    if (timeoutEnv)
+    {
+        timeout = osgEarth::as<long>(std::string(timeoutEnv), 0);
+    }
+    OE_DEBUG << LC << "Setting timeout to " << timeout << std::endl;
+    curl_easy_setopt( _curl_handle, CURLOPT_TIMEOUT, timeout );
+    long connectTimeout = s_connectTimeout;
+    const char* connectTimeoutEnv = getenv("OSGEARTH_HTTP_CONNECTTIMEOUT");
+    if (connectTimeoutEnv)
+    {
+        connectTimeout = osgEarth::as<long>(std::string(connectTimeoutEnv), 0);
+    }
+    OE_DEBUG << LC << "Setting connect timeout to " << connectTimeout << std::endl;
+    curl_easy_setopt( _curl_handle, CURLOPT_CONNECTTIMEOUT, connectTimeout );
+
+    _initialized = true;
+}
+
+HTTPClient::~HTTPClient()
+{
+    if (_curl_handle) curl_easy_cleanup( _curl_handle );
+    _curl_handle = 0;
+}
+
+void
+HTTPClient::setProxySettings( const ProxySettings& proxySettings )
+{
+    s_proxySettings = proxySettings;
+}
+
+const std::string& HTTPClient::getUserAgent()
+{
+    return s_userAgent;
+}
+
+void  HTTPClient::setUserAgent(const std::string& userAgent)
+{
+    s_userAgent = userAgent;
+}
+
+long HTTPClient::getTimeout()
+{
+    return s_timeout;
+}
+
+void HTTPClient::setTimeout( long timeout )
+{
+    s_timeout = timeout;
+}
+
+long HTTPClient::getConnectTimeout()
+{
+    return s_connectTimeout;
+}
+
+void HTTPClient::setConnectTimeout( long timeout )
+{
+    s_connectTimeout = timeout;
+}
+URLRewriter* HTTPClient::getURLRewriter()
+{
+    return s_rewriter.get();
+}
+
+void HTTPClient::setURLRewriter( URLRewriter* rewriter )
+{
+    s_rewriter = rewriter;
+}
+
+CurlConfigHandler* HTTPClient::getCurlConfigHandler()
+{
+    return s_curlConfigHandler.get();
+}
+
+void HTTPClient::setCurlConfighandler(CurlConfigHandler* handler)
+{
+    s_curlConfigHandler = handler;
+}
+
+void
+HTTPClient::globalInit()
+{
+    curl_global_init(CURL_GLOBAL_ALL);
+}
+
+void
+HTTPClient::readOptions(const osgDB::Options* options, std::string& proxy_host, std::string& proxy_port) const
+{
+    // try to set proxy host/port by reading the CURL proxy options
+    if ( options )
+    {
+        std::istringstream iss( options->getOptionString() );
+        std::string opt;
+        while( iss >> opt )
+        {
+            int index = opt.find( "=" );
+            if( opt.substr( 0, index ) == "OSG_CURL_PROXY" )
+            {
+                proxy_host = opt.substr( index+1 );
+            }
+            else if ( opt.substr( 0, index ) == "OSG_CURL_PROXYPORT" )
+            {
+                proxy_port = opt.substr( index+1 );
+            }
+        }
+    }
+}
+
+bool
+HTTPClient::decodeMultipartStream(const std::string&   boundary,
+                                  HTTPResponse::Part*  input,
+                                  HTTPResponse::Parts& output) const
+{
+    std::string bstr = std::string("--") + boundary;
+    std::string line;
+    char tempbuf[256];
+
+    // first thing in the stream should be the boundary.
+    input->_stream.read( tempbuf, bstr.length() );
+    tempbuf[bstr.length()] = 0;
+    line = tempbuf;
+    if ( line != bstr )
+    {
+        OE_INFO << LC
+            << "decodeMultipartStream: protocol violation; "
+            << "expecting boundary; instead got: \""
+            << line
+            << "\"" << std::endl;
+        return false;
+    }
+
+    for( bool done=false; !done; )
+    {
+        osg::ref_ptr<HTTPResponse::Part> next_part = new HTTPResponse::Part();
+
+        // first finish off the boundary.
+        std::getline( input->_stream, line );
+        if ( line == "--" )
+        {
+            done = true;
+        }
+        else
+        {
+            // read all headers. this ends with a blank line.
+            line = " ";
+            while( line.length() > 0 && !done )
+            {
+                std::getline( input->_stream, line );
+
+                // check for EOS:
+                if ( line == "--" )
+                {
+                    done = true;
+                }
+                else
+                {
+                    StringTokenizer tok(":");
+                    StringVector tized;
+                    tok.tokenize(line, tized);
+                    if ( tized.size() >= 2 )
+                        next_part->_headers[tized[0]] = tized[1];
+                }
+            }
+        }
+
+        if ( !done )
+        {
+            // read data until we reach the boundary
+            unsigned int bstr_ptr = 0;
+            std::string temp;
+            //unsigned int c = 0;
+            while( bstr_ptr < bstr.length() )
+            {
+                char b;
+                input->_stream.read( &b, 1 );
+                if ( b == bstr[bstr_ptr] )
+                {
+                    bstr_ptr++;
+                }
+                else
+                {
+                    for( unsigned int i=0; i<bstr_ptr; i++ )
+                    {
+                        next_part->_stream << bstr[i];
+                    }
+                    next_part->_stream << b;
+                    next_part->_size += bstr_ptr + 1;
+                    bstr_ptr = 0;
+                }
+            }
+            output.push_back( next_part.get() );
+        }
+    }
+
+    return true;
+}
+
+HTTPResponse
+HTTPClient::get( const HTTPRequest&    request,
+                 const osgDB::Options* options,
+                 ProgressCallback*     progress)
+{
+    return getClient().doGet( request, options, progress );
+}
+
+HTTPResponse
+HTTPClient::get( const std::string&    url,
+                 const osgDB::Options* options,
+                 ProgressCallback*     progress)
+{
+    return getClient().doGet( url, options, progress);
+}
+
+ReadResult
+HTTPClient::readImage(const HTTPRequest&    request,
+                      const osgDB::Options* options,
+                      ProgressCallback*     progress)
+{
+    return getClient().doReadImage( request, options, progress );
+}
+
+ReadResult
+HTTPClient::readNode(const HTTPRequest&    request,
+                     const osgDB::Options* options,
+                     ProgressCallback*     progress)
+{
+    return getClient().doReadNode( request, options, progress );
+}
+
+ReadResult
+HTTPClient::readObject(const HTTPRequest&    request,
+                       const osgDB::Options* options,
+                       ProgressCallback*     progress)
+{
+    return getClient().doReadObject( request, options, progress );
+}
+
+ReadResult
+HTTPClient::readString(const HTTPRequest&    request,
+                       const osgDB::Options* options,
+                       ProgressCallback*     progress)
+{
+    return getClient().doReadString( request, options, progress );
+}
+
+bool
+HTTPClient::download(const std::string& uri,
+                     const std::string& localPath)
+{
+    return getClient().doDownload( uri, localPath );
+}
+
+
+#ifdef OSGEARTH_USE_WININET_FOR_HTTP
+
+namespace
+{
+    std::string GetLastErrorAsString()
+    {
+        //Get the error message, if any.
+        DWORD errorMessageID = ::GetLastError();
+        if(errorMessageID == 0)
+            return std::string("Error Code 0"); //No error message has been recorded
+
+        LPSTR messageBuffer = nullptr;
+        size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                                     NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
+
+        std::string message(messageBuffer, size);
+
+        //Free the buffer.
+        LocalFree(messageBuffer);
+
+        message = Stringify() << "[Code " << errorMessageID << "] " << message;
+
+        return message;
+    }
+}
+
+HTTPResponse
+HTTPClient::doGet(const HTTPRequest&    request,
+                  const osgDB::Options* options,
+                  ProgressCallback*     progress) const
+{
+    METRIC_BEGIN("HTTPClient::doGet", 1,
+                   "url", request.getURL().c_str());
+
+    OE_START_TIMER(http_get);
+
+    std::string url = request.getURL();
+    // Rewrite the url if the url rewriter is available
+    osg::ref_ptr< URLRewriter > rewriter = getURLRewriter();
+    if ( rewriter.valid() )
+    {
+        std::string oldURL = url;
+        url = rewriter->rewrite( oldURL );
+        OE_DEBUG << LC << "Rewrote URL " << oldURL << " to " << url << std::endl;
+    }
+
+    HINTERNET hInternet = InternetOpen(
+        getUserAgent().c_str(),
+        //"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko",
+        INTERNET_OPEN_TYPE_PRECONFIG,
+        NULL,       // proxy
+        NULL,       // proxy bypass
+        0);         // flags
+
+    if( !hInternet )
+    {
+        OE_WARN << LC << "InternetOpen failed: " << GetLastErrorAsString() << std::endl;
+        return HTTPResponse(0);
+    }
+
+    // clears any session cookies..?
+    // Commented this out, because is invalidates authorization caching:
+    //InternetSetOption( 0, INTERNET_OPTION_END_BROWSER_SESSION, NULL, 0 );
+
+    // parse the URL:
+    URL_COMPONENTS urlcomp;
+    ZeroMemory(&urlcomp, sizeof(urlcomp));
+	urlcomp.dwStructSize = sizeof(urlcomp);
+	urlcomp.dwHostNameLength = 1;
+    urlcomp.dwUserNameLength = 1;
+    urlcomp.dwPasswordLength = 1;
+    urlcomp.dwUrlPathLength  = 1;
+
+    if ( !InternetCrackUrl(url.c_str(), 0, 0L, &urlcomp) )
+    {
+        OE_WARN << LC << "InternetCrackUrl failed for " << url << ": " << GetLastErrorAsString() << std::endl;
+        InternetCloseHandle( hInternet );
+        return HTTPResponse(0);
+    }
+
+    WORD port =
+        urlcomp.nPort != 0 ? urlcomp.nPort :
+        urlcomp.nScheme == INTERNET_SCHEME_HTTPS ? INTERNET_DEFAULT_HTTPS_PORT :
+        INTERNET_DEFAULT_HTTP_PORT;
+
+    std::string hostName( urlcomp.lpszHostName );
+    int slash = hostName.find_first_of('/');
+    if ( slash != std::string::npos )
+        hostName = hostName.substr(0, slash);
+
+    OE_DEBUG
+        << "\n"
+        << "Host name = " << hostName << "\n"
+        << "Url path = " << urlcomp.lpszUrlPath << "\n"
+        << "Port = " << port << "\n";
+
+    DWORD openFlags =
+        //INTERNET_FLAG_PRAGMA_NOCACHE |
+        INTERNET_FLAG_RELOAD |
+        INTERNET_FLAG_NO_CACHE_WRITE |
+        INTERNET_FLAG_KEEP_CONNECTION;
+
+    if ( urlcomp.nScheme == INTERNET_SCHEME_HTTPS)
+    {
+        openFlags |= INTERNET_FLAG_SECURE;
+    }
+
+    // InternetOpenUrl is a lot less code, but we have to use the InternetConnnect +
+    // HttpOpenRequest + HttpSendRequest approach in order to support system dialogs
+    // for PKI certificates and username/password queries.
+
+    HINTERNET hConnection = InternetConnect(
+        hInternet,
+        hostName.c_str(),
+        port,
+        "", // username
+        "", // password
+        INTERNET_SERVICE_HTTP,
+        0,  // flags
+        INTERNET_NO_CALLBACK); // context
+
+    if ( !hConnection )
+    {
+        OE_WARN << LC << "InternetConnect failed for " << url << ": " << GetLastErrorAsString() << std::endl;
+        InternetCloseHandle( hInternet );
+        return HTTPResponse(0);
+    }
+
+    HINTERNET hRequest = HttpOpenRequest(
+        hConnection,            // handle from InternetConnect
+        "GET",                  // verb
+        urlcomp.lpszUrlPath,    // path
+        NULL,                   // HTTP version (NULL = default)
+        NULL,                   // Referrer
+        NULL,                   // Accept types
+        openFlags,              // flags
+        INTERNET_NO_CALLBACK);                  // context (user data)
+
+    if ( !hRequest )
+    {
+        OE_WARN << LC << "HttpOpenRequest failed for " << url << ": " << GetLastErrorAsString() << std::endl;
+        InternetCloseHandle( hConnection );
+        InternetCloseHandle( hInternet );
+        return HTTPResponse(0);
+    }
+
+    while( !HttpSendRequest(hRequest, NULL, 0, NULL, 0) )
+    {
+        DWORD errorNum = GetLastError();
+
+        // Request for client cert; open system dialog.
+        if ( errorNum == ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED )
+        {
+            OE_WARN << LC << "Server reports ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED!\n";
+
+            // Return ERROR_SUCCESS regardless of clicking on OK or Cancel
+            DWORD dialogResult = InternetErrorDlg(
+                GetDesktopWindow(),
+                hRequest,
+                ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
+                FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
+                NULL );
+
+            if ( dialogResult != ERROR_SUCCESS )
+            {
+                OE_WARN << LC << "InternetErrorDlg failed to produce client cert " << url << ": " << GetLastErrorAsString() << std::endl;
+                InternetCloseHandle( hRequest );
+                InternetCloseHandle( hConnection );
+                InternetCloseHandle( hInternet );
+                return HTTPResponse(0);
+            }
+        }
+
+        else
+        {
+            OE_WARN << LC << "HttpSendRequest failed to open " << url << ": " << GetLastErrorAsString() << std::endl;
+            InternetCloseHandle( hRequest );
+            InternetCloseHandle( hConnection );
+            InternetCloseHandle( hInternet );
+            return HTTPResponse(0);
+        }
+    }
+
+    int statusCode = 0;
+    std::string contentType;
+
+    char  buffer[4096];
+    DWORD bufferLen = 4096;
+    DWORD index = 0;
+
+    if ( HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, buffer, &bufferLen, &index) )
+    {
+        statusCode = as<int>( std::string(buffer, bufferLen), 0 );
+    }
+
+    HTTPResponse response(statusCode);
+
+    bufferLen = 4096, index = 0;
+    if ( HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_TYPE, buffer, &bufferLen, &index) )
+    {
+        response._mimeType = std::string(buffer, bufferLen);
+    }
+
+    bufferLen = 4096, index = 0;
+    if ( HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED, buffer, &bufferLen, &index) )
+    {
+        response._lastModified = as<long>(std::string(buffer, bufferLen), 0L);
+    }
+
+    response._mimeType = contentType;
+
+    if ( statusCode == 200 )
+    {
+        osg::ref_ptr<HTTPResponse::Part> part = new HTTPResponse::Part();
+
+        DWORD numBytesRead = 0;
+        while( InternetReadFile(hRequest, buffer, 4096, &numBytesRead) && numBytesRead )
+        {
+            part->_stream << std::string(buffer, numBytesRead);
+        }
+
+        response._parts.push_back( part.get() );
+    }
+
+    InternetCloseHandle( hRequest );
+    InternetCloseHandle( hConnection );
+    InternetCloseHandle( hInternet );
+
+    response._duration_s = OE_STOP_TIMER(http_get);
+
+    if ( progress )
+    {
+        progress->stats("http_get_time") += OE_GET_TIMER(http_get);
+        progress->stats("http_get_count") += 1;
+        if ( response._cancelled )
+            progress->stats("http_cancel_count") += 1;
+    }
+
+    METRIC_END("HTTPClient::doGet", 1,
+                 "response_code", toString<int>(response.getCode()).c_str());
+
+    return response;
+}
+
+#else // OSGEARTH_USE_WININET_FOR_HTTP
+
+HTTPResponse
+HTTPClient::doGet(const HTTPRequest&    request,
+                  const osgDB::Options* options,
+                  ProgressCallback*     progress) const
+{
+    METRIC_BEGIN("HTTPClient::doGet", 1,
+                   "url", request.getURL().c_str());
+
+    initialize();
+
+    OE_START_TIMER(http_get);
+
+    std::string url = request.getURL();
+
+    const osgDB::AuthenticationMap* authenticationMap = (options && options->getAuthenticationMap()) ?
+            options->getAuthenticationMap() :
+            osgDB::Registry::instance()->getAuthenticationMap();
+
+    std::string proxy_host;
+    std::string proxy_port = "8080";
+
+    std::string proxy_auth;
+
+    //TODO: don't do all this proxy setup on every GET. Just do it once per client, or only when
+    // the proxy information changes.
+
+    //Try to get the proxy settings from the global settings
+    if (s_proxySettings.isSet())
+    {
+        proxy_host = s_proxySettings.get().hostName();
+        std::stringstream buf;
+        buf << s_proxySettings.get().port();
+        proxy_port = buf.str();
+
+        std::string proxy_username = s_proxySettings.get().userName();
+        std::string proxy_password = s_proxySettings.get().password();
+        if (!proxy_username.empty() && !proxy_password.empty())
+        {
+            proxy_auth = proxy_username + std::string(":") + proxy_password;
+        }
+    }
+
+    //Try to get the proxy settings from the local options that are passed in.
+    readOptions( options, proxy_host, proxy_port );
+
+    optional< ProxySettings > proxySettings;
+    ProxySettings::fromOptions( options, proxySettings );
+    if (proxySettings.isSet())
+    {
+        proxy_host = proxySettings.get().hostName();
+        proxy_port = toString<int>(proxySettings.get().port());
+        OE_DEBUG << LC << "Read proxy settings from options " << proxy_host << " " << proxy_port << std::endl;
+    }
+
+    //Try to get the proxy settings from the environment variable
+    const char* proxyEnvAddress = getenv("OSG_CURL_PROXY");
+    if (proxyEnvAddress) //Env Proxy Settings
+    {
+        proxy_host = std::string(proxyEnvAddress);
+
+        const char* proxyEnvPort = getenv("OSG_CURL_PROXYPORT"); //Searching Proxy Port on Env
+        if (proxyEnvPort)
+        {
+            proxy_port = std::string( proxyEnvPort );
+        }
+    }
+
+    const char* proxyEnvAuth = getenv("OSGEARTH_CURL_PROXYAUTH");
+    if (proxyEnvAuth)
+    {
+        proxy_auth = std::string(proxyEnvAuth);
+    }
+
+    // Set up proxy server:
+    std::string proxy_addr;
+    if ( !proxy_host.empty() )
+    {
+        std::stringstream buf;
+        buf << proxy_host << ":" << proxy_port;
+        std::string bufStr;
+        bufStr = buf.str();
+        proxy_addr = bufStr;
+
+        if ( s_HTTP_DEBUG )
+        {
+            OE_NOTICE << LC << "Using proxy: " << proxy_addr << std::endl;
+        }
+
+        //curl_easy_setopt( _curl_handle, CURLOPT_HTTPPROXYTUNNEL, 1 );
+        curl_easy_setopt( _curl_handle, CURLOPT_PROXY, proxy_addr.c_str() );
+
+        //Setup the proxy authentication if setup
+        if (!proxy_auth.empty())
+        {
+            if ( s_HTTP_DEBUG )
+            {
+                OE_NOTICE << LC << "Using proxy authentication "