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