openModeller
Version 1.4.0
|
00001 00029 #include <openmodeller/Environment.hh> 00030 00031 #include <openmodeller/os_specific.hh> 00032 #include <openmodeller/Log.hh> 00033 #include <openmodeller/env_io/Map.hh> 00034 #include <openmodeller/env_io/RasterFactory.hh> 00035 #include <openmodeller/env_io/GeoTransform.hh> 00036 #include <openmodeller/Random.hh> 00037 #include <openmodeller/Configuration.hh> 00038 #include <openmodeller/Occurrence.hh> 00039 #include <openmodeller/Exceptions.hh> 00040 00041 #if defined (HAVE_VALUES_H) && !defined(WIN32) 00042 #include <values.h> 00043 #else 00044 #include <float.h> 00045 #define MAXFLOAT FLT_MAX 00046 #endif 00047 00048 using std::string; 00049 using std::vector; 00050 00051 #undef DEBUG_GET 00052 00053 /****************************************************************/ 00054 /*********************** factory methods ************************/ 00055 EnvironmentPtr createEnvironment( const std::vector<std::string>& categs, 00056 const std::vector<std::string>& maps, 00057 const std::string& mask_file ) 00058 { 00059 return EnvironmentPtr( new EnvironmentImpl( categs, maps, mask_file ) ); 00060 } 00061 00062 EnvironmentPtr createEnvironment( const std::vector<std::string>& categs, 00063 const std::vector<std::string>& maps ) 00064 { 00065 return EnvironmentPtr( new EnvironmentImpl( categs, maps, "" ) ); 00066 } 00067 00068 EnvironmentPtr createEnvironment( const ConstConfigurationPtr& config ) 00069 { 00070 EnvironmentPtr env( new EnvironmentImpl() ); 00071 00072 env->setConfiguration( config ); 00073 00074 return env; 00075 } 00076 00077 EnvironmentPtr createEnvironment( ) 00078 { 00079 return EnvironmentPtr( new EnvironmentImpl( ) ); 00080 } 00081 00082 00083 /****************************************************************/ 00084 /******************* static utility functions *******************/ 00085 00086 ConfigurationPtr 00087 EnvironmentImpl::getLayerConfig( const layer& l, bool basicConfig ) { 00088 00089 ConfigurationPtr cfg( new ConfigurationImpl() ); 00090 00091 cfg->addNameValue( "Id", l.first ); 00092 00093 if ( ! basicConfig ) { 00094 00095 cfg->addNameValue( "IsCategorical", l.second->isCategorical() ); 00096 00097 if ( l.second->hasMinMax() ) { 00098 00099 Scalar min; 00100 Scalar max; 00101 l.second->getMinMax( &min, &max ); 00102 cfg->addNameValue( "Min", min ); 00103 cfg->addNameValue( "Max", max ); 00104 } 00105 } 00106 00107 return cfg; 00108 } 00109 00110 EnvironmentImpl::layer 00111 EnvironmentImpl::makeLayer( const ConstConfigurationPtr& config, Map *map ) { 00112 00113 string filename = config->getAttribute( "Id" ); 00114 // map should already have the categorical attribute set! 00115 //int categ = config->getAttributeAsInt( "IsCategorical", 0 ); 00116 00117 layer l = makeLayer( filename, map ); 00118 00119 try { 00120 00121 // The calls to getAttribute( string ) will throw if 00122 // the attribute is not found. 00123 config->getAttribute("Min"); 00124 config->getAttribute("Max"); 00125 00126 // If we make it here, the attributes exist, and the 00127 // hard coded default value of 0.0 will not be used. 00128 double min = config->getAttributeAsDouble( "Min", 0.0 ); 00129 double max = config->getAttributeAsDouble( "Max", 0.0 ); 00130 00131 l.second->setMinMax( min, max ); 00132 } 00133 catch (AttributeNotFound& e) { 00134 00135 UNUSED(e); 00136 } 00137 00138 return l; 00139 } 00140 00141 EnvironmentImpl::layer 00142 EnvironmentImpl::makeLayer( const string& filename, int categ ) { 00143 00144 Map *map = new Map( RasterFactory::instance().create( filename, categ ) ); 00145 00146 return makeLayer( filename, map ); 00147 } 00148 00149 EnvironmentImpl::layer 00150 EnvironmentImpl::makeLayer( const string& filename, Map *map ) { 00151 00152 layer l; 00153 00154 if ( !map ) { 00155 00156 Log::instance()->warn( "Cannot read environment file: '%s'\n", filename.c_str() ); 00157 } 00158 else { 00159 00160 l.first = filename; 00161 l.second = map; 00162 } 00163 00164 return l; 00165 } 00166 00167 00168 /****************************************************************/ 00169 /************************* EnvironmentImpl **************************/ 00170 00171 /*******************/ 00172 /*** constructor ***/ 00173 00174 EnvironmentImpl::EnvironmentImpl() : 00175 _layers(), 00176 _mask(), 00177 _xmin(0), 00178 _ymin(0), 00179 _xmax(0), 00180 _ymax(0), 00181 _normalizerPtr(0) 00182 { 00183 } 00184 00185 EnvironmentImpl::EnvironmentImpl( const std::vector<std::string>& categs, 00186 const std::vector<std::string>& maps, 00187 const std::string& mask ) 00188 { 00189 initialize( categs, maps, mask ); 00190 } 00191 00192 void 00193 EnvironmentImpl::initialize( const std::vector<std::string>& categs, 00194 const std::vector<std::string>& maps, 00195 const std::string& mask ) 00196 { 00197 _normalizerPtr = 0; 00198 00199 // Initialize mask and read its region. 00200 changeMask( mask ); 00201 changeLayers( categs, maps ); 00202 } 00203 00204 00205 /******************/ 00206 /*** destructor ***/ 00207 00208 EnvironmentImpl::~EnvironmentImpl() 00209 { 00210 clearLayers(); 00211 clearMask(); 00212 00213 if ( _normalizerPtr ) { 00214 00215 delete _normalizerPtr; 00216 } 00217 } 00218 00219 00220 /*****************/ 00221 /*** deep copy ***/ 00222 00223 EnvironmentImpl* 00224 EnvironmentImpl::clone() const 00225 { 00226 std::vector<std::string> categs; 00227 std::vector<std::string> maps; 00228 00229 layers::const_iterator lay = _layers.begin(); 00230 layers::const_iterator end = _layers.end(); 00231 00232 while ( lay != end ) { 00233 00234 if ( lay->second->isCategorical() ) { 00235 00236 categs.push_back( lay->first ); 00237 } 00238 else { 00239 00240 maps.push_back( lay->first ); 00241 } 00242 00243 ++lay; 00244 } 00245 00246 EnvironmentImpl* clone = new EnvironmentImpl( categs, maps, _mask.first ); 00247 00248 if ( _normalizerPtr ) { 00249 00250 clone->normalize( _normalizerPtr ); 00251 } 00252 00253 return clone; 00254 } 00255 00256 void 00257 EnvironmentImpl::clearLayers() { 00258 if (_layers.size() < 1) { 00259 return; 00260 } 00261 layers::iterator first = _layers.begin(); 00262 layers::iterator end = _layers.end(); 00263 for ( ; first != end; ++ first ) { 00264 delete (*first).second; 00265 } 00266 _layers.clear(); 00267 } 00268 00269 void 00270 EnvironmentImpl::clearMask() { 00271 if ( _mask.second ) 00272 delete _mask.second; 00273 00274 _mask.first = ""; 00275 _mask.second = 0; 00276 } 00277 00278 /*********************/ 00279 /*** configuration ***/ 00280 00281 ConfigurationPtr 00282 EnvironmentImpl::getConfiguration() const 00283 { 00284 ConfigurationPtr config( new ConfigurationImpl("Environment") ); 00285 00286 config->addNameValue( "NumLayers", (int) _layers.size() ); 00287 00288 layers::const_iterator l = _layers.begin(); 00289 layers::const_iterator end = _layers.end(); 00290 00291 for( ; l != end; ++l ) { 00292 00293 ConfigurationPtr cfg( getLayerConfig( *l ) ); 00294 cfg->setName("Map"); 00295 config->addSubsection( cfg ); 00296 } 00297 00298 if ( _mask.second ) { 00299 00300 bool onlyBasicConfig = true; 00301 ConfigurationPtr maskcfg( getLayerConfig( _mask, onlyBasicConfig ) ); 00302 maskcfg->setName( "Mask" ); 00303 config->addSubsection( maskcfg ); 00304 } 00305 00306 return config; 00307 } 00308 00309 void 00310 EnvironmentImpl::setConfiguration( const ConstConfigurationPtr & config ) 00311 { 00312 clearMask(); 00313 clearLayers(); 00314 00315 // Store configuration references for each layer 00316 // Important: users can mix continuous and categorical layers, but oM 00317 // requires categorical layers to be first, so having two 00318 // vectors can handle this. 00319 std::vector<ConstConfigurationPtr> categ_layer_confs; 00320 std::vector<ConstConfigurationPtr> cont_layer_confs; 00321 00322 // Need to have separate containers, since loadLayers requires 00323 // knowing if the layer is categorical or not. 00324 std::vector<string> categ_layer_ids; 00325 std::vector<Map*> categ_layer_refs; 00326 std::vector<string> cont_layer_ids; 00327 std::vector<Map*> cont_layer_refs; 00328 00329 // Mask config 00330 ConstConfigurationPtr mask_conf = config->getSubsection( "Mask", false ); 00331 std::vector<string> mask_id; 00332 std::vector<Map*> mask_ref; 00333 00334 // Suck in all the filenames. 00335 Configuration::subsection_list subs = config->getAllSubsections(); 00336 Configuration::subsection_list::const_iterator it = subs.begin(); 00337 while( it != subs.end() ) { 00338 00339 string subname = (*it)->getName(); 00340 string id = (*it)->getAttribute("Id"); 00341 00342 if ( subname == "Mask" && mask_id.size() == 0 ) { 00343 00344 // Mask 00345 00346 mask_id.push_back(id); 00347 mask_ref.push_back(0); 00348 } 00349 else { 00350 00351 // Environmental layer 00352 00353 int categ = (*it)->getAttributeAsInt( "IsCategorical", 0 ); 00354 00355 if ( categ == 0 ) { 00356 00357 cont_layer_ids.push_back(id); 00358 cont_layer_refs.push_back(0); 00359 const ConstConfigurationPtr lconf = (*it); 00360 cont_layer_confs.push_back(lconf); 00361 } 00362 else { 00363 00364 categ_layer_ids.push_back(id); 00365 categ_layer_refs.push_back(0); 00366 const ConstConfigurationPtr lconf = (*it); 00367 categ_layer_confs.push_back(lconf); 00368 } 00369 } 00370 00371 ++it; 00372 } 00373 00374 loadLayers( categ_layer_ids, categ_layer_refs, 1 ); 00375 loadLayers( cont_layer_ids, cont_layer_refs, 0 ); 00376 00377 // Fill _layers attribute 00378 for( unsigned int i = 0; i< categ_layer_confs.size(); i++ ) { 00379 00380 // Call makeLayer with the config object! This implementation is more complete 00381 // than just calling it with id and categorical attributes. 00382 _layers.push_back( makeLayer( categ_layer_confs[i], categ_layer_refs[i] ) ); 00383 } 00384 00385 for( unsigned int i = 0; i< cont_layer_confs.size(); i++ ) { 00386 00387 // Call makeLayer with the config object! This implementation is more complete 00388 // than just calling it with id and categorical attributes. 00389 _layers.push_back( makeLayer( cont_layer_confs[i], cont_layer_refs[i] ) ); 00390 } 00391 00392 // Assign mask 00393 if ( mask_id.size() ) { 00394 00395 loadLayers( mask_id, mask_ref, 0 ); 00396 _mask = makeLayer( mask_conf, mask_ref[0] ); 00397 } 00398 00399 calcRegion(); 00400 } 00401 00402 /*******************/ 00403 /*** load Layers ***/ 00404 void 00405 EnvironmentImpl::loadLayers( const std::vector<string>& map_ids, std::vector<Map*>& map_refs, int categ ) 00406 { 00407 bool unfinished_loading = true; 00408 bool got_one = false; 00409 00410 while ( unfinished_loading ) { 00411 00412 unfinished_loading = false; 00413 got_one = false; 00414 00415 for( unsigned int i = 0; i< map_ids.size(); i++ ) { 00416 00417 if ( map_refs[i] == 0 ) { 00418 00419 try { 00420 00421 map_refs[i] = new Map( RasterFactory::instance().create( map_ids[i], categ ) ); 00422 got_one = true; 00423 } 00424 catch ( RasterException& e ) { 00425 00426 if ( e.getCode() == 1 ) { 00427 00428 // layer is being downloaded by other process, 00429 // so skip it for now and try others 00430 unfinished_loading = true; 00431 } 00432 else { 00433 00434 throw; 00435 } 00436 } 00437 } 00438 } 00439 00440 if ( unfinished_loading && !got_one ) { 00441 00442 Sleep(5000); 00443 } 00444 } 00445 } 00446 00447 /*********************/ 00448 /*** change Layers ***/ 00449 int 00450 EnvironmentImpl::changeLayers( const std::vector<std::string>& categs, 00451 const std::vector<std::string>& maps ) 00452 { 00453 if ( ! (categs.size() + maps.size()) ) 00454 return 0; 00455 00456 clearLayers(); 00457 00458 // STL maps with layer ids (unique) pointing to Map*s 00459 00460 // categorical data 00461 std::vector<Map*> categ_layer_refs; 00462 for( unsigned int i = 0; i< categs.size(); i++ ) { 00463 00464 categ_layer_refs.push_back(0); 00465 } 00466 00467 loadLayers( categs, categ_layer_refs, 1 ); 00468 00469 // continuous data 00470 std::vector<Map*> cont_layer_refs; 00471 for( unsigned int i = 0; i< maps.size(); i++ ) { 00472 00473 cont_layer_refs.push_back(0); 00474 } 00475 00476 loadLayers( maps, cont_layer_refs, 0 ); 00477 00478 // Fill _layers attribute 00479 for( unsigned int i = 0; i< categs.size(); i++ ) { 00480 00481 _layers.push_back( makeLayer(categs[i], categ_layer_refs[i]) ); 00482 } 00483 00484 for( unsigned int i = 0; i< maps.size(); i++ ) { 00485 00486 _layers.push_back( makeLayer(maps[i], cont_layer_refs[i]) ); 00487 } 00488 00489 calcRegion(); 00490 00491 return categs.size() + maps.size(); 00492 } 00493 00494 00495 /*******************/ 00496 /*** change Mask ***/ 00497 int 00498 EnvironmentImpl::changeMask( const std::string& mask_file ) 00499 { 00500 int ret = 1; 00501 00502 clearMask(); 00503 00504 // New mask 00505 if ( !mask_file.empty() ) { 00506 00507 bool unfinished_loading = true; 00508 00509 while ( unfinished_loading ) { 00510 00511 unfinished_loading = false; 00512 00513 try { 00514 00515 _mask = makeLayer( mask_file, 0 ); 00516 } 00517 catch ( RasterException& e ) { 00518 00519 if ( e.getCode() == 1 ) { 00520 00521 // mask is being downloaded by other process 00522 unfinished_loading = true; 00523 Sleep(5000); 00524 } 00525 else { 00526 00527 throw; 00528 } 00529 } 00530 } 00531 00532 if ( !_mask.second ) { 00533 00534 ret = 0; 00535 } 00536 } 00537 00538 calcRegion(); 00539 00540 return ret; 00541 } 00542 00543 00544 /******************************/ 00545 /*** num Categorical Layers ***/ 00546 size_t 00547 EnvironmentImpl::numCategoricalLayers() const 00548 { 00549 size_t size = 0; 00550 00551 layers::const_iterator lay = _layers.begin(); 00552 layers::const_iterator end = _layers.end(); 00553 00554 while ( lay != end && lay->second->isCategorical() ) { 00555 00556 ++size; 00557 ++lay; 00558 } 00559 00560 return size; 00561 } 00562 00563 00564 /*****************/ 00565 /*** get Type ***/ 00566 int 00567 EnvironmentImpl::isCategorical( int i ) 00568 { 00569 return _layers[i].second->isCategorical(); 00570 } 00571 00572 00573 /******************/ 00574 /*** get MinMax ***/ 00575 void 00576 EnvironmentImpl::getMinMax( Sample * min, Sample * max ) const 00577 { 00578 int i = 0; 00579 00580 layers::const_iterator lay = _layers.begin(); 00581 layers::const_iterator end = _layers.end(); 00582 00583 while ( lay != end ) { 00584 00585 Map *map = lay->second; 00586 00587 Scalar mapMin, mapMax; 00588 map->getMinMax( &mapMin, &mapMax ); 00589 (*min)[i] = mapMin; 00590 (*max)[i] = mapMax; 00591 00592 ++lay; 00593 i++; 00594 } 00595 } 00596 00597 /*****************/ 00598 /*** normalize ***/ 00599 void 00600 EnvironmentImpl::normalize( Normalizer * normalizerPtr ) { 00601 00602 if ( normalizerPtr ) { 00603 00604 _normalizerPtr = normalizerPtr->getCopy(); 00605 } 00606 else { 00607 00608 resetNormalization(); 00609 } 00610 } 00611 00612 00613 /***************************/ 00614 /*** reset Normalization ***/ 00615 void 00616 EnvironmentImpl::resetNormalization() { 00617 00618 if ( _normalizerPtr ) { 00619 00620 delete _normalizerPtr; 00621 00622 _normalizerPtr = 0; 00623 } 00624 } 00625 00626 /*********************************/ 00627 /*** get Unnormalized Internal ***/ 00628 void 00629 EnvironmentImpl::getUnnormalizedInternal( Sample *sample, Coord x, Coord y ) const 00630 { 00631 // layers and the mask, if possible. 00632 if ( ! checkCoordinates( x, y ) ) { 00633 #if defined(DEBUG_GET) 00634 Log::instance()->debug( "EnvironmentImpl::get() Coordinate (%f,%f) is not in common region\n",x,y); 00635 #endif 00636 return; 00637 } 00638 00639 // Create the return value. 00640 sample->resize( _layers.size() ); 00641 00642 // Read variables values from the layers. 00643 layers::const_iterator lay = _layers.begin(); 00644 layers::const_iterator end = _layers.end(); 00645 Sample::iterator s = sample->begin(); 00646 00647 while ( lay != end ) { 00648 00649 if ( ! lay->second->get( x, y, s ) ) { 00650 #if defined(DEBUG_GET) 00651 Log::instance()->debug( "EnvironmentImpl::get() Coordinate (%f,%f) does not have data in layer %s\n",x,y,lay->first.c_str()); 00652 #endif 00653 sample->resize(0); 00654 return; 00655 } 00656 ++lay; 00657 ++s; 00658 } 00659 } 00660 00661 Sample 00662 EnvironmentImpl::getUnnormalized( Coord x, Coord y ) const 00663 { 00664 Sample sample; 00665 getUnnormalizedInternal( &sample, x, y ); 00666 return sample; 00667 } 00668 00669 Sample 00670 EnvironmentImpl::getNormalized( Coord x, Coord y ) const 00671 { 00672 Sample sample; 00673 getUnnormalizedInternal( &sample, x, y); 00674 00675 sample.setCategoricalThreshold( numCategoricalLayers() ); 00676 00677 if ( _normalizerPtr ) { 00678 00679 _normalizerPtr->normalize( &sample ); 00680 } 00681 00682 return sample; 00683 } 00684 00685 Sample 00686 EnvironmentImpl::get( Coord x, Coord y ) const 00687 { 00688 if ( _normalizerPtr ) { 00689 00690 return getNormalized(x,y); 00691 } 00692 else { 00693 00694 return getUnnormalized(x,y); 00695 } 00696 } 00697 00698 Sample 00699 EnvironmentImpl::getRandom( Coord *xout, Coord *yout ) const 00700 { 00701 Random myrand; 00702 Coord x, y; 00703 00704 Sample s; 00705 00706 int max_loop = 5000; 00707 00708 int loop = 0; 00709 00710 do { 00711 00712 x = myrand( _xmin, _xmax ); 00713 y = myrand( _ymin, _ymax ); 00714 00715 s = get( x, y ); 00716 00717 loop++; 00718 00719 } while ( s.size() == 0 && loop < max_loop ); 00720 00721 if ( loop == max_loop ) { 00722 00723 std::string msg = "Exceeded maximum number of attempts to generate pseudo point.\n"; 00724 00725 Log::instance()->error( msg.c_str() ); 00726 00727 throw OmException( msg ); 00728 } 00729 00730 if ( xout != 0 ) 00731 *xout = x; 00732 if ( yout != 0 ) 00733 *yout = y; 00734 00735 return s; 00736 } 00737 00738 00739 /*************************/ 00740 /*** check Coordinates ***/ 00741 int 00742 EnvironmentImpl::checkCoordinates( Coord x, Coord y ) const 00743 { 00744 // Accept the point, regardless of mask, if 00745 // it falls in a common region among all layers. 00746 if ( x < _xmin || x > _xmax || y < _ymin || y > _ymax ) { 00747 #if defined(DEBUG_GET) 00748 Log::instance()->debug( "EnvironmentImpl::checkCoordinates() Coordinate (%f,%f) not in extent of all regions\n",x,y); 00749 #endif 00750 00751 return 0; 00752 } 00753 00754 // If there's no mask, accept the point. 00755 if ( ! _mask.second ) { 00756 00757 return 1; 00758 } 00759 00760 Scalar val; 00761 00762 bool hasmaskevalue = ( _mask.second->get( x, y, &val ) > 0 ); 00763 00764 if ( ! hasmaskevalue ) { 00765 #if defined(DEBUG_GET) 00766 Log::instance()->debug( "EnvironmentImpl::check() Coordinate (%f,%f) has no mask value\n",x,y); 00767 #endif 00768 } 00769 00770 return hasmaskevalue; 00771 } 00772 00773 00774 /******************/ 00775 /*** get Region ***/ 00776 int 00777 EnvironmentImpl::getRegion( Coord *xmin, Coord *ymin, Coord *xmax, Coord *ymax ) const 00778 { 00779 *xmin = _xmin; 00780 *ymin = _ymin; 00781 *xmax = _xmax; 00782 *ymax = _ymax; 00783 00784 return 1; 00785 } 00786 00787 int 00788 EnvironmentImpl::getExtremes( Sample* min, Sample* max ) const 00789 { 00790 int nlayers = _layers.size(); 00791 min->resize( nlayers ); 00792 max->resize( nlayers ); 00793 00794 layers::const_iterator map = _layers.begin(); 00795 00796 for ( int i = 0; i < nlayers; i++ ) { 00797 Scalar amin, amax; 00798 map->second->getMinMax( &amin, &amax ); 00799 (*min)[i] = amin; 00800 (*max)[i] = amax; 00801 ++map; 00802 } 00803 00804 if ( _normalizerPtr ) { 00805 00806 _normalizerPtr->normalize( min ); 00807 _normalizerPtr->normalize( max ); 00808 } 00809 00810 return 1; 00811 } 00812 00813 00814 /*******************/ 00815 /*** calc Region ***/ 00816 void 00817 EnvironmentImpl::calcRegion() 00818 { 00819 Coord xmin, ymin, xmax, ymax; 00820 00821 _xmin = _ymin = -MAXFLOAT; 00822 _xmax = _ymax = MAXFLOAT; 00823 00824 // The mask region is the default. 00825 if ( _mask.second ) 00826 _mask.second->getExtent( &_xmin, &_ymin, &_xmax, &_ymax ); 00827 00828 // Crop region to fit all layers. 00829 layers::const_iterator lay = _layers.begin(); 00830 layers::const_iterator end = _layers.end(); 00831 while ( lay != end ) { 00832 lay->second->getExtent( &xmin, &ymin, &xmax, &ymax ); 00833 ++lay; 00834 00835 if ( xmin > _xmin ) 00836 _xmin = xmin; 00837 00838 if ( ymin > _ymin ) 00839 _ymin = ymin; 00840 00841 if ( xmax < _xmax ) 00842 _xmax = xmax; 00843 00844 if ( ymax < _ymax ) 00845 _ymax = ymax; 00846 } 00847 00848 if ( (_xmin >= _xmax) || ( _ymin >= _ymax ) ) { 00849 Log::instance()->warn( "Maps intersection is empty (boundaries: xmin=%f, xmax=%f, ymin=%f, ymax=%f)\n", _xmin, _xmax, _ymin, _ymax ); 00850 } 00851 } 00852 00853 00854 /********************/ 00855 /*** remove Layer ***/ 00856 void 00857 EnvironmentImpl::removeLayer(unsigned int index) 00858 { 00859 if ( index >= numLayers() ) { 00860 00861 return; 00862 } 00863 00864 layers::iterator it = _layers.begin(); 00865 00866 unsigned int tmp = 0; 00867 00868 while ( tmp < index ) { 00869 00870 ++tmp; 00871 ++it; 00872 } 00873 00874 _layers.erase( it ); 00875 00876 calcRegion(); 00877 }