1 // terrasync.cxx -- scenery fetcher
3 // Started by Curtis Olson, November 2002.
5 // Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
6 // Copyright (C) 2008 Alexander R. Perry <alex.perry@ieee.org>
7 // Copyright (C) 2011 Thorsten Brehm <brehmt@gmail.com>
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // General Public License for more details.
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 # include <simgear_config.h>
29 #include <simgear/compiler.h>
38 #elif defined(_MSC_VER)
40 # ifndef HAVE_SVN_CLIENT_H
46 #include <stdlib.h> // atoi() atof() abs() system()
47 #include <signal.h> // signal()
55 #include <simgear/compiler.h>
57 #include "terrasync.hxx"
58 #include <simgear/bucket/newbucket.hxx>
59 #include <simgear/misc/sg_path.hxx>
60 #include <simgear/misc/strutils.hxx>
61 #include <simgear/threads/SGQueue.hxx>
62 #include <simgear/misc/sg_dir.hxx>
64 #ifdef HAVE_SVN_CLIENT_H
65 # ifdef HAVE_LIBSVN_CLIENT_1
66 # include <svn_version.h>
67 # include <svn_auth.h>
68 # include <svn_client.h>
69 # include <svn_cmdline.h>
70 # include <svn_pools.h>
72 # undef HAVE_SVN_CLIENT_H
76 #ifdef HAVE_SVN_CLIENT_H
77 static const svn_version_checklist_t mysvn_checklist[] = {
78 { "svn_subr", svn_subr_version },
79 { "svn_client", svn_client_version },
82 static const bool svn_built_in_available = true;
84 static const bool svn_built_in_available = false;
87 using namespace simgear;
90 const char* rsync_cmd =
91 "rsync --verbose --archive --delete --perms --owner --group";
93 const char* svn_options =
96 namespace UpdateInterval
98 // interval in seconds to allow an update to repeat after a successful update (=daily)
99 static const double SuccessfulAttempt = 24*60*60;
100 // interval in seconds to allow another update after a failed attempt (10 minutes)
101 static const double FailedAttempt = 10*60;
104 typedef map<string,time_t> CompletedTiles;
106 ///////////////////////////////////////////////////////////////////////////////
107 // helper functions ///////////////////////////////////////////////////////////
108 ///////////////////////////////////////////////////////////////////////////////
109 string stripPath(string path)
111 // svn doesn't like trailing white-spaces or path separators - strip them!
112 path = simgear::strutils::strip(path);
113 size_t slen = path.length();
115 ((path[slen-1]=='/')||(path[slen-1]=='\\')))
119 return path.substr(0,slen);
122 bool hasWhitespace(string path)
124 return path.find(' ')!=string::npos;
127 ///////////////////////////////////////////////////////////////////////////////
128 // WaitingTile ////////////////////////////////////////////////////////////////
129 ///////////////////////////////////////////////////////////////////////////////
133 WaitingTile(string dir,bool refresh) :
134 _dir(dir), _refreshScenery(refresh) {}
136 bool _refreshScenery;
139 ///////////////////////////////////////////////////////////////////////////////
140 // SGTerraSync::SvnThread /////////////////////////////////////////////////////
141 ///////////////////////////////////////////////////////////////////////////////
142 class SGTerraSync::SvnThread : public SGThread
146 virtual ~SvnThread( ) { stop(); }
151 bool isIdle() {return waitingTiles.empty();}
152 void request(const WaitingTile& dir) {waitingTiles.push_front(dir);}
153 bool isDirty() { bool r = _is_dirty;_is_dirty = false;return r;}
154 bool hasNewTiles() { return !_freshTiles.empty();}
155 WaitingTile getNewTile() { return _freshTiles.pop_front();}
157 void setSvnServer(string server) { _svn_server = stripPath(server);}
158 void setExtSvnUtility(string svn_util) { _svn_command = simgear::strutils::strip(svn_util);}
159 void setRsyncServer(string server) { _rsync_server = simgear::strutils::strip(server);}
160 void setLocalDir(string dir) { _local_dir = stripPath(dir);}
161 string getLocalDir() { return _local_dir;}
162 void setUseSvn(bool use_svn) { _use_svn = use_svn;}
163 void setAllowedErrorCount(int errors) {_allowed_errors = errors;}
165 #ifdef HAVE_SVN_CLIENT_H
166 void setUseBuiltin(bool built_in) { _use_built_in = built_in;}
169 volatile bool _active;
170 volatile bool _running;
172 volatile bool _stalled;
173 volatile int _fail_count;
174 volatile int _updated_tile_count;
175 volatile int _success_count;
176 volatile int _consecutive_errors;
177 volatile int _allowed_errors;
181 bool syncTree(const char* dir, bool& isNewDirectory);
182 bool syncTreeExternal(const char* dir);
184 #ifdef HAVE_SVN_CLIENT_H
185 static int svnClientSetup(void);
186 bool syncTreeInternal(const char* dir);
190 // Things we need for doing subversion checkout - often
191 static apr_pool_t *_svn_pool;
192 static svn_client_ctx_t *_svn_ctx;
193 static svn_opt_revision_t *_svn_rev;
194 static svn_opt_revision_t *_svn_rev_peg;
197 volatile bool _is_dirty;
199 SGBlockingDeque <WaitingTile> waitingTiles;
200 CompletedTiles _completedTiles;
201 SGBlockingDeque <WaitingTile> _freshTiles;
205 string _rsync_server;
209 #ifdef HAVE_SVN_CLIENT_H
210 apr_pool_t* SGTerraSync::SvnThread::_svn_pool = NULL;
211 svn_client_ctx_t* SGTerraSync::SvnThread::_svn_ctx = NULL;
212 svn_opt_revision_t* SGTerraSync::SvnThread::_svn_rev = NULL;
213 svn_opt_revision_t* SGTerraSync::SvnThread::_svn_rev_peg = NULL;
216 SGTerraSync::SvnThread::SvnThread() :
222 _updated_tile_count(0),
224 _consecutive_errors(0),
226 #ifdef HAVE_SVN_CLIENT_H
233 #ifdef HAVE_SVN_CLIENT_H
234 int errCode = SGTerraSync::SvnThread::svnClientSetup();
235 if (errCode != EXIT_SUCCESS)
237 SG_LOG(SG_TERRAIN,SG_ALERT,
238 "Failed to initialize built-in SVN client, error = " << errCode);
243 void SGTerraSync::SvnThread::stop()
245 // drop any pending requests
246 waitingTiles.clear();
251 // set stop flag and wake up the thread with an empty request
253 WaitingTile w("",false);
259 bool SGTerraSync::SvnThread::start()
266 SG_LOG(SG_TERRAIN,SG_ALERT,
267 "Cannot start scenery download. Local cache directory is undefined.");
273 SGPath path(_local_dir);
276 SG_LOG(SG_TERRAIN,SG_ALERT,
277 "Cannot start scenery download. Directory '" << _local_dir <<
278 "' does not exist. Set correct directory path or create directory folder.");
284 path.append("version");
287 SG_LOG(SG_TERRAIN,SG_ALERT,
288 "Cannot start scenery download. Directory '" << _local_dir <<
289 "' contains the base package. Use a separate directory.");
295 #ifdef HAVE_SVN_CLIENT_H
296 _use_svn |= _use_built_in;
299 if ((_use_svn)&&(_svn_server==""))
301 SG_LOG(SG_TERRAIN,SG_ALERT,
302 "Cannot start scenery download. Subversion scenery server is undefined.");
307 if ((!_use_svn)&&(_rsync_server==""))
309 SG_LOG(SG_TERRAIN,SG_ALERT,
310 "Cannot start scenery download. Rsync scenery server is undefined.");
317 _updated_tile_count = 0;
319 _consecutive_errors = 0;
325 #ifdef HAVE_SVN_CLIENT_H
326 if (_use_svn && _use_built_in)
327 status = "Using built-in SVN support. ";
332 status = "Using external SVN utility '";
333 status += _svn_command;
338 status = "Using RSYNC. ";
341 // not really an alert - but we want to (always) see this message, so user is
342 // aware we're downloading scenery (and using bandwidth).
343 SG_LOG(SG_TERRAIN,SG_ALERT,
344 "Starting automatic scenery download/synchronization. "
346 << "Directory: '" << _local_dir << "'.");
352 // sync one directory tree
353 bool SGTerraSync::SvnThread::syncTree(const char* dir, bool& isNewDirectory)
356 SGPath path( _local_dir );
359 isNewDirectory = !path.exists();
362 rc = path.create_dir( 0755 );
365 SG_LOG(SG_TERRAIN,SG_ALERT,
366 "Cannot create directory '" << dir << "', return code = " << rc );
371 #ifdef HAVE_SVN_CLIENT_H
373 return syncTreeInternal(dir);
377 return syncTreeExternal(dir);
382 #ifdef HAVE_SVN_CLIENT_H
383 bool SGTerraSync::SvnThread::syncTreeInternal(const char* dir)
385 SG_LOG(SG_TERRAIN,SG_DEBUG, "Synchronizing scenery directory " << dir);
388 SG_LOG(SG_TERRAIN,SG_ALERT,
389 "Built-in SVN client failed to initialize.");
393 ostringstream command;
394 command << _svn_server << "/" << dir;
396 ostringstream dest_base_dir;
397 dest_base_dir << _local_dir << "/" << dir;
399 apr_pool_t *subpool = svn_pool_create(_svn_pool);
401 svn_error_t *err = NULL;
402 #if (SVN_VER_MINOR >= 5)
403 err = svn_client_checkout3(NULL,
404 command.str().c_str(),
405 dest_base_dir.str().c_str(),
409 0, // ignore-externals = false
410 0, // allow unver obstructions = false
415 err = svn_client_checkout2(NULL,
416 command.str().c_str(),
417 dest_base_dir.str().c_str(),
420 1, // recurse=true - same as svn_depth_infinity for checkout3 above
421 0, // ignore externals = false
426 bool ReturnValue = true;
429 // Report errors from the checkout attempt
430 if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
432 // ignore errors when remote path doesn't exist (no scenery data for ocean areas)
436 SG_LOG(SG_TERRAIN,SG_ALERT,
437 "Failed to synchronize directory '" << dir << "', " <<
438 err->message << " (code " << err->apr_err << ").");
439 svn_error_clear(err);
441 err = svn_client_cleanup(dest_base_dir.str().c_str(),
445 SG_LOG(SG_TERRAIN,SG_ALERT,
446 "SVN repository cleanup successful for '" << dir << "'.");
452 SG_LOG(SG_TERRAIN,SG_DEBUG, "Done with scenery directory " << dir);
454 svn_pool_destroy(subpool);
459 bool SGTerraSync::SvnThread::syncTreeExternal(const char* dir)
462 SGPath localPath( _local_dir );
463 localPath.append( dir );
467 buf << "\"" << _svn_command << "\" "
468 << svn_options << " "
469 << "\"" << _svn_server << "/" << dir << "\" "
470 << "\"" << localPath.str_native() << "\"";
472 buf << rsync_cmd << " "
473 << "\"" << _rsync_server << "/" << dir << "/\" "
474 << "\"" << localPath.str_native() << "/\"";
479 // windows command line parsing is just lovely...
480 // to allow white spaces, the system call needs this:
481 // ""C:\Program Files\something.exe" somearg "some other arg""
482 // Note: whitespace strings quoted by a pair of "" _and_ the
483 // entire string needs to be wrapped by "" too.
484 // The svn url needs forward slashes (/) as a path separator while
485 // the local path needs windows-native backslash as a path separator.
486 command = "\"" + buf.str() + "\"";
490 SG_LOG(SG_TERRAIN,SG_DEBUG, "sync command '" << command << "'");
493 // tbd: does Windows support "popen"?
494 int rc = system( command.c_str() );
496 FILE* pipe = popen( command.c_str(), "r");
498 // wait for external process to finish
505 SG_LOG(SG_TERRAIN,SG_ALERT,
506 "Failed to synchronize directory '" << dir << "', " <<
507 "error code= " << rc);
513 void SGTerraSync::SvnThread::run()
518 WaitingTile next = waitingTiles.pop_front();
522 CompletedTiles::iterator ii =
523 _completedTiles.find( next._dir );
524 time_t now = time(0);
525 if ((ii == _completedTiles.end())||
528 bool isNewDirectory = false;
531 if (!syncTree(next._dir.c_str(),isNewDirectory))
533 _consecutive_errors++;
535 _completedTiles[ next._dir ] = now + UpdateInterval::FailedAttempt;
539 _consecutive_errors = 0;
541 SG_LOG(SG_TERRAIN,SG_INFO,
542 "Successfully synchronized directory '" << next._dir << "'");
543 if (next._refreshScenery)
546 _updated_tile_count++;
549 // for now only report new directories to refresh display
550 // (i.e. only when ocean needs to be replaced with actual data)
551 _freshTiles.push_back(next);
555 _completedTiles[ next._dir ] = now + UpdateInterval::SuccessfulAttempt;
560 if ((_allowed_errors >= 0)&&
561 (_consecutive_errors >= _allowed_errors))
573 #ifdef HAVE_SVN_CLIENT_H
574 // Configure our subversion session
575 int SGTerraSync::SvnThread::svnClientSetup(void)
577 // Are we already prepared?
578 if (_svn_pool) return EXIT_SUCCESS;
579 // No, so initialize svn internals generally
582 // there is a segfault when providing an error stream.
583 // Apparently, calling setvbuf with a nul buffer is
584 // not supported under msvc 7.1 ( code inside svn_cmdline_init )
585 if (svn_cmdline_init("terrasync", 0) != EXIT_SUCCESS)
588 // revert locale setting
589 setlocale(LC_ALL,"C");
591 /* svn_cmdline_init configures the locale. Setup environment to ensure the
592 * default "C" locale remains active, since fgfs isn't locale aware - especially
593 * requires "." as decimal point in strings containing floating point varibales. */
594 setenv("LC_ALL", "C", 1);
596 if (svn_cmdline_init("terrasync", stderr) != EXIT_SUCCESS)
601 apr_pool_create(&pool, NULL);
602 svn_error_t *err = NULL;
603 SVN_VERSION_DEFINE(_svn_version);
604 err = svn_ver_check_list(&_svn_version, mysvn_checklist);
606 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
607 err = svn_ra_initialize(pool);
609 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
610 char *config_dir = NULL;
611 err = svn_config_ensure(config_dir, pool);
613 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
614 err = svn_client_create_context(&_svn_ctx, pool);
616 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
617 err = svn_config_get_config(&(_svn_ctx->config),
620 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
622 cfg = ( svn_config_t*) apr_hash_get(
624 SVN_CONFIG_CATEGORY_CONFIG,
625 APR_HASH_KEY_STRING);
627 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
629 svn_auth_baton_t *ab=NULL;
631 #if (SVN_VER_MINOR >= 6)
632 err = svn_cmdline_create_auth_baton (&ab,
633 TRUE, NULL, NULL, config_dir, TRUE, FALSE, cfg,
634 _svn_ctx->cancel_func, _svn_ctx->cancel_baton, pool);
636 err = svn_cmdline_setup_auth_baton(&ab,
637 TRUE, NULL, NULL, config_dir, TRUE, cfg,
638 _svn_ctx->cancel_func, _svn_ctx->cancel_baton, pool);
642 return svn_cmdline_handle_exit_error(err, pool, "fgfs: ");
644 _svn_ctx->auth_baton = ab;
645 #if (SVN_VER_MINOR >= 5)
646 _svn_ctx->conflict_func = NULL;
647 _svn_ctx->conflict_baton = NULL;
650 // Now our magic revisions
651 _svn_rev = (svn_opt_revision_t*) apr_palloc(pool,
652 sizeof(svn_opt_revision_t));
655 _svn_rev_peg = (svn_opt_revision_t*) apr_palloc(pool,
656 sizeof(svn_opt_revision_t));
659 _svn_rev->kind = svn_opt_revision_head;
660 _svn_rev_peg->kind = svn_opt_revision_unspecified;
661 // Success if we got this far
667 ///////////////////////////////////////////////////////////////////////////////
668 // SGTerraSync ////////////////////////////////////////////////////////////////
669 ///////////////////////////////////////////////////////////////////////////////
670 SGTerraSync::SGTerraSync(SGPropertyNode_ptr root) :
674 _terraRoot(root->getNode("/sim/terrasync",true)),
678 _svnThread = new SvnThread();
681 SGTerraSync::~SGTerraSync()
683 _tiedProperties.Untie();
688 void SGTerraSync::init()
690 _refreshDisplay = _terraRoot->getNode("refresh-display",true);
691 _terraRoot->setBoolValue("built-in-svn-available",svn_built_in_available);
695 void SGTerraSync::reinit()
697 // do not reinit when enabled and we're already up and running
698 if ((_terraRoot->getBoolValue("enabled",false))&&
699 (_svnThread->_active && _svnThread->_running))
704 if (_terraRoot->getBoolValue("enabled",false))
706 _svnThread->setSvnServer(_terraRoot->getStringValue("svn-server",""));
707 _svnThread->setRsyncServer(_terraRoot->getStringValue("rsync-server",""));
708 _svnThread->setLocalDir(_terraRoot->getStringValue("scenery-dir",""));
709 _svnThread->setAllowedErrorCount(_terraRoot->getIntValue("max-errors",5));
711 #ifdef HAVE_SVN_CLIENT_H
712 _svnThread->setUseBuiltin(_terraRoot->getBoolValue("use-built-in-svn",true));
714 _terraRoot->setBoolValue("use-built-in-svn",false);
716 _svnThread->setUseSvn(_terraRoot->getBoolValue("use-svn",true));
717 _svnThread->setExtSvnUtility(_terraRoot->getStringValue("ext-svn-utility","svn"));
719 if (_svnThread->start())
721 syncAirportsModels();
722 if (last_lat != NOWHERE && last_lon != NOWHERE)
724 // reschedule most recent position
729 schedulePosition(lat, lon);
734 _stalledNode->setBoolValue(_svnThread->_stalled);
737 void SGTerraSync::bind()
739 _tiedProperties.Tie( _terraRoot->getNode("busy", true), (bool*) &_svnThread->_busy );
740 _tiedProperties.Tie( _terraRoot->getNode("active", true), (bool*) &_svnThread->_active );
741 _tiedProperties.Tie( _terraRoot->getNode("update-count", true), (int*) &_svnThread->_success_count );
742 _tiedProperties.Tie( _terraRoot->getNode("error-count", true), (int*) &_svnThread->_fail_count );
743 _tiedProperties.Tie( _terraRoot->getNode("tile-count", true), (int*) &_svnThread->_updated_tile_count );
744 _terraRoot->getNode("busy", true)->setAttribute(SGPropertyNode::WRITE,false);
745 _terraRoot->getNode("active", true)->setAttribute(SGPropertyNode::WRITE,false);
746 _terraRoot->getNode("update-count", true)->setAttribute(SGPropertyNode::WRITE,false);
747 _terraRoot->getNode("error-count", true)->setAttribute(SGPropertyNode::WRITE,false);
748 _terraRoot->getNode("tile-count", true)->setAttribute(SGPropertyNode::WRITE,false);
749 _terraRoot->getNode("use-built-in-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false);
750 _terraRoot->getNode("use-svn", true)->setAttribute(SGPropertyNode::USERARCHIVE,false);
751 // stalled is used as a signal handler (to connect listeners triggering GUI pop-ups)
752 _stalledNode = _terraRoot->getNode("stalled", true);
753 _stalledNode->setBoolValue(_svnThread->_stalled);
754 _stalledNode->setAttribute(SGPropertyNode::PRESERVE,true);
757 void SGTerraSync::unbind()
760 _tiedProperties.Untie();
763 void SGTerraSync::update(double)
765 static SGBucket bucket;
766 if (_svnThread->isDirty())
768 if (!_svnThread->_active)
770 if (_svnThread->_stalled)
772 SG_LOG(SG_TERRAIN,SG_ALERT,
773 "Automatic scenery download/synchronization stalled. Too many errors.");
777 // not really an alert - just always show this message
778 SG_LOG(SG_TERRAIN,SG_ALERT,
779 "Automatic scenery download/synchronization has stopped.");
781 _stalledNode->setBoolValue(_svnThread->_stalled);
784 if (!_refreshDisplay->getBoolValue())
787 while (_svnThread->hasNewTiles())
789 WaitingTile next = _svnThread->getNewTile();
790 if (next._refreshScenery)
792 refreshScenery(_svnThread->getLocalDir(),next._dir);
798 void SGTerraSync::refreshScenery(SGPath path,const string& relativeDir)
800 // find tiles to be refreshed
803 path.append(relativeDir);
806 simgear::Dir dir(path);
807 //TODO need to be smarter here. only update tiles which actually
808 // changed recently. May also be possible to use information from the
809 // built-in SVN client directly (instead of checking directory contents).
810 PathList tileList = dir.children(simgear::Dir::TYPE_FILE, ".stg");
811 for (unsigned int i=0; i<tileList.size(); ++i)
813 // reload scenery tile
814 long index = atoi(tileList[i].file().c_str());
815 _refreshCb(_userCbData, index);
821 bool SGTerraSync::isIdle() {return _svnThread->isIdle();}
823 void SGTerraSync::setTileRefreshCb(SGTerraSyncCallback refreshCb, void* userCbData)
825 _refreshCb = refreshCb;
826 _userCbData = userCbData;
829 void SGTerraSync::syncAirportsModels()
831 static const char* bounds = "MZAJKL"; // airport sync order: K-L, A-J, M-Z
832 // note "request" method uses LIFO order, i.e. processes most recent request first
833 for( unsigned i = 0; i < strlen(bounds)/2; i++ )
835 for ( char synced_other = bounds[2*i]; synced_other <= bounds[2*i+1]; synced_other++ )
838 dir << "Airports/" << synced_other;
839 WaitingTile w(dir.str(),false);
840 _svnThread->request( w );
843 WaitingTile w("Models",false);
844 _svnThread->request( w );
848 void SGTerraSync::syncArea( int lat, int lon )
850 if ( lat < -90 || lat > 90 || lon < -180 || lon > 180 )
853 int baselat, baselon;
856 int base = (int)(lat / 10);
857 if ( lat == base * 10 ) {
860 baselat = (base - 1) * 10;
864 baselat = (int)(lat / 10) * 10;
868 int base = (int)(lon / 10);
869 if ( lon == base * 10 ) {
872 baselon = (base - 1) * 10;
876 baselon = (int)(lon / 10) * 10;
880 const char* terrainobjects[3] = { "Terrain", "Objects", 0 };
882 for (const char** tree = &terrainobjects[0]; *tree; tree++)
885 dir << *tree << "/" << setfill('0')
886 << EW << setw(3) << abs(baselon) << NS << setw(2) << abs(baselat) << "/"
887 << EW << setw(3) << abs(lon) << NS << setw(2) << abs(lat);
888 WaitingTile w(dir.str(),refresh);
889 _svnThread->request( w );
895 void SGTerraSync::syncAreas( int lat, int lon, int lat_dir, int lon_dir )
897 if ( lat_dir == 0 && lon_dir == 0 ) {
898 // do surrounding 8 1x1 degree areas.
899 for ( int i = lat - 1; i <= lat + 1; ++i ) {
900 for ( int j = lon - 1; j <= lon + 1; ++j ) {
901 if ( i != lat || j != lon ) {
907 if ( lat_dir != 0 ) {
908 syncArea( lat + lat_dir, lon - 1 );
909 syncArea( lat + lat_dir, lon + 1 );
910 syncArea( lat + lat_dir, lon );
912 if ( lon_dir != 0 ) {
913 syncArea( lat - 1, lon + lon_dir );
914 syncArea( lat + 1, lon + lon_dir );
915 syncArea( lat, lon + lon_dir );
919 // do current 1x1 degree area first
920 syncArea( lat, lon );
924 bool SGTerraSync::schedulePosition(int lat, int lon)
928 // Ignore messages where the location does not change
929 if ( lat != last_lat || lon != last_lon )
931 if (_svnThread->_running)
933 SG_LOG(SG_TERRAIN,SG_DEBUG, "Requesting scenery update for position " <<
937 if ( last_lat != NOWHERE && last_lon != NOWHERE )
939 int dist = lat - last_lat;
942 lat_dir = dist / abs(dist);
948 dist = lon - last_lon;
951 lon_dir = dist / abs(dist);
958 SG_LOG(SG_TERRAIN,SG_DEBUG, "Scenery update for " <<
959 "lat = " << lat << ", lon = " << lon <<
960 ", lat_dir = " << lat_dir << ", " <<
961 "lon_dir = " << lon_dir);
963 syncAreas( lat, lon, lat_dir, lon_dir );