1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3 * Copyright 2019 Pelican Mapping
4 * http://osgearth.org
5 *
6 * osgEarth is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17 * IN THE SOFTWARE.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21 */
22 #include <osgEarthUtil/TMSBackFiller>
23 #include <osgEarth/FileUtils>
24 #include <osgEarth/ImageMosaic>
25 
26 #include <osgDB/FileUtils>
27 #include <osgDB/FileNameUtils>
28 #include <osgDB/ReadFile>
29 #include <osgDB/WriteFile>
30 
31 #define LC "[TMSBackFiller] "
32 
33 using namespace osgEarth::Util;
34 using namespace osgEarth;
35 
TMSBackFiller()36 TMSBackFiller::TMSBackFiller() :
37 _minLevel(0u),
38 _maxLevel(0u),
39 _verbose(false)
40 {
41     //nop
42 }
43 
44 
process(const std::string & tms,osgDB::Options * options)45 void TMSBackFiller::process( const std::string& tms, osgDB::Options* options )
46 {
47     std::string fullPath = getFullPath( "", tms );
48     _options = options;
49 
50     //Read the tilemap
51     _tileMap = TileMapReaderWriter::read( fullPath, 0 );
52     if (_tileMap)
53     {
54         //The max level is where we are going to read data from, so we need to start one level up.
55         osg::ref_ptr< const Profile> profile = _tileMap->createProfile();
56 
57         //If the bounds aren't valid just use the full extent of the profile.
58         if (!_bounds.valid())
59         {
60             _bounds = profile->getExtent().bounds();
61         }
62 
63 
64         int firstLevel = _maxLevel-1;
65 
66         GeoExtent extent( profile->getSRS(), _bounds );
67 
68         //Process each level in it's entirety
69         for (int level = firstLevel; level >= static_cast<int>(_minLevel); level--)
70         {
71             if (_verbose) OE_NOTICE << "Processing level " << level << std::endl;
72 
73             TileKey ll = profile->createTileKey(extent.xMin(), extent.yMin(), level);
74             TileKey ur = profile->createTileKey(extent.xMax(), extent.yMax(), level);
75 
76             for (unsigned int x = ll.getTileX(); x <= ur.getTileX(); x++)
77             {
78                 for (unsigned int y = ur.getTileY(); y <= ll.getTileY(); y++)
79                 {
80                     TileKey key = TileKey(level, x, y, profile.get());
81                     processKey( key );
82                 }
83             }
84 
85         }
86     }
87     else
88     {
89         OE_NOTICE << "Failed to load TileMap from " << _tmsPath << std::endl;
90     }
91 }
92 
processKey(const TileKey & key)93 void TMSBackFiller::processKey( const TileKey& key )
94 {
95     if (_verbose) OE_NOTICE << "Processing key " << key.str() << std::endl;
96 
97     //Get all of the child tiles for this key, load them and mosaic them into a new tile
98     TileKey ulKey = key.createChildKey( 0 );
99     TileKey urKey = key.createChildKey( 1 );
100     TileKey llKey = key.createChildKey( 2 );
101     TileKey lrKey = key.createChildKey( 3 );
102 
103     osg::ref_ptr< osg::Image > ul = readTile( ulKey );
104     osg::ref_ptr< osg::Image > ur = readTile( urKey );
105     osg::ref_ptr< osg::Image > ll = readTile( llKey );
106     osg::ref_ptr< osg::Image > lr = readTile( lrKey );
107 
108     if (ul.valid() && ur.valid() && ll.valid() && lr.valid())
109     {
110         //Merge them together
111         ImageMosaic mosaic;
112         mosaic.getImages().push_back( TileImage( ul.get(), ulKey ) );
113         mosaic.getImages().push_back( TileImage( ur.get(), urKey ) );
114         mosaic.getImages().push_back( TileImage( ll.get(), llKey ) );
115         mosaic.getImages().push_back( TileImage( lr.get(), lrKey ) );
116 
117         osg::ref_ptr< osg::Image> merged = mosaic.createImage();
118         if (merged.valid())
119         {
120             //Resize the image so it's the same size as one of the input files
121             osg::ref_ptr<osg::Image> resized;
122             ImageUtils::resizeImage( merged.get(), ul->s(), ul->t(), resized );
123             std::string outputFilename = getFilename( key );
124             writeTile( key, resized.get() );
125         }
126     }
127 }
128 
getFilename(const TileKey & key)129 std::string TMSBackFiller::getFilename( const TileKey& key )
130 {
131     return _tileMap->getURL( key, false );
132 }
133 
readTile(const TileKey & key)134 osg::Image* TMSBackFiller::readTile( const TileKey& key )
135 {
136     std::string filename = getFilename( key );
137     osg::ref_ptr< osg::Image> image = osgDB::readRefImageFile( filename );
138     return image.release();
139 }
140 
writeTile(const TileKey & key,osg::Image * image)141 void TMSBackFiller::writeTile( const TileKey& key, osg::Image* image )
142 {
143     std::string filename = getFilename( key );
144     if ( !osgDB::fileExists( osgDB::getFilePath(filename) ) )
145         osgEarth::makeDirectoryForFile( filename );
146     osgDB::writeImageFile( *image, filename, _options.get() );
147 }
148 
149