2
* libpal - Automated Placement of Labels Library
4
* Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
5
* University of Applied Sciences, Western Switzerland
9
* maxence.laurent <at> heig-vd <dot> ch
11
* eric.taillard <at> heig-vd <dot> ch
13
* This file is part of libpal.
15
* libpal is free software: you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation, either version 3 of the License, or
18
* (at your option) any later version.
20
* libpal is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with libpal. If not, see <http://www.gnu.org/licenses/>.
34
#define _CRT_SECURE_NO_DEPRECATE
45
#include <pal/layer.h>
46
#include <pal/palexception.h>
47
#include <pal/internalexception.h>
49
#include "linkedlist.hpp"
50
#include "hashtable.hpp"
53
#include "geomfunction.h"
56
#include "simplemutex.h"
61
Layer::Layer( const char *lyrName, double min_scale, double max_scale, Arrangement arrangement, Units label_unit, double defaultPriority, bool obstacle, bool active, bool toLabel, Pal *pal )
62
: pal( pal ), obstacle( obstacle ), active( active ),
63
toLabel( toLabel ), label_unit( label_unit ),
64
min_scale( min_scale ), max_scale( max_scale ),
65
arrangement( arrangement ), arrangementFlags( 0 ), mode( LabelPerFeature ), mergeLines( false )
68
this->name = new char[strlen( lyrName ) +1];
69
strcpy( this->name, lyrName );
71
modMutex = new SimpleMutex();
73
rtree = new RTree<FeaturePart*, double, 2, double>();
74
hashtable = new HashTable<Feature*> ( 5281 );
76
connectedHashtable = new HashTable< LinkedList<FeaturePart*>* > ( 5391 );
77
connectedTexts = new LinkedList< char* >( strCompare );
79
if ( defaultPriority < 0.0001 )
80
this->defaultPriority = 0.0001;
81
else if ( defaultPriority > 1.0 )
82
this->defaultPriority = 1.0;
84
this->defaultPriority = defaultPriority;
86
featureParts = new LinkedList<FeaturePart*> ( ptrFeaturePartCompare );
87
features = new LinkedList<Feature*> ( ptrFeatureCompare );
96
while ( featureParts->size() )
98
delete featureParts->pop_front();
104
// this hashtable and list should be empty if they still exist
105
delete connectedHashtable;
107
// features in the hashtable
110
while ( features->size() )
112
delete features->pop_front();
126
Feature* Layer::getFeature( const char* geom_id )
128
Feature** fptr = hashtable->find( geom_id );
129
return ( fptr ? *fptr : NULL );
133
bool Layer::isScaleValid( double scale )
135
return ( scale >= min_scale || min_scale == -1 )
136
&& ( scale <= max_scale || max_scale == -1 );
140
int Layer::getNbFeatures()
142
return features->size();
145
const char *Layer::getName()
150
Arrangement Layer::getArrangement()
155
void Layer::setArrangement( Arrangement arrangement )
157
this->arrangement = arrangement;
161
bool Layer::isObstacle()
166
bool Layer::isToLabel()
171
bool Layer::isActive()
177
double Layer::getMinScale()
182
double Layer::getMaxScale()
187
double Layer::getPriority()
189
return defaultPriority;
192
void Layer::setObstacle( bool obstacle )
194
this->obstacle = obstacle;
197
void Layer::setActive( bool active )
199
this->active = active;
202
void Layer::setToLabel( bool toLabel )
204
this->toLabel = toLabel;
207
void Layer::setMinScale( double min_scale )
209
this->min_scale = min_scale;
212
void Layer::setMaxScale( double max_scale )
214
this->max_scale = max_scale;
217
void Layer::setPriority( double priority )
219
if ( priority >= 1.0 ) // low priority
220
defaultPriority = 1.0;
221
else if ( priority <= 0.0001 )
222
defaultPriority = 0.0001; // high priority
224
defaultPriority = priority;
229
bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText )
231
if ( !geom_id || label_x < 0 || label_y < 0 )
236
if ( hashtable->find( geom_id ) )
239
throw new PalException::FeatureExists();
242
// Split MULTI GEOM and Collection in simple geometries
243
GEOSGeometry *the_geom = userGeom->getGeosGeometry();
245
Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y );
247
bool first_feat = true;
249
double geom_size = -1, biggest_size = -1;
250
FeaturePart* biggest_part = NULL;
252
// break the (possibly multi-part) geometry into simple geometries
253
LinkedList <const GEOSGeometry*> *simpleGeometries = unmulti( the_geom );
255
while ( simpleGeometries->size() > 0 )
257
const GEOSGeometry* geom = simpleGeometries->pop_front();
259
// ignore invalid geometries (e.g. polygons with self-intersecting rings)
260
if ( GEOSisValid( geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
262
std::cerr << "ignoring invalid feature " << geom_id << std::endl;
266
int type = GEOSGeomTypeId( geom );
268
if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
269
throw InternalException::UnknownGeometry();
271
FeaturePart* fpart = new FeaturePart( f, geom );
273
// ignore invalid geometries
274
if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
275
( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
281
// polygons: reorder coordinates
282
if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
288
if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
290
if ( type == GEOS_LINESTRING )
291
GEOSLength( geom, &geom_size );
292
else if ( type == GEOS_POLYGON )
293
GEOSArea( geom, &geom_size );
295
if ( geom_size > biggest_size )
297
biggest_size = geom_size;
298
delete biggest_part; // safe with NULL part
299
biggest_part = fpart;
301
continue; // don't add the feature part now, do it later
302
// TODO: we should probably add also other parts to act just as obstacles
305
// feature part is ready!
306
addFeaturePart( fpart, labelText );
310
delete simpleGeometries;
312
userGeom->releaseGeosGeometry( the_geom );
316
// if using only biggest parts...
317
if ( mode == LabelPerFeature && biggest_part != NULL )
319
addFeaturePart( biggest_part, labelText );
323
// add feature to layer if we have added something
326
features->push_back( f );
327
hashtable->insertItem( geom_id, f );
334
return !first_feat; // true if we've added something
337
void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
341
fpart->getBoundingBox( bmin, bmax );
343
// add to list of layer's feature parts
344
featureParts->push_back( fpart );
346
// add to r-tree for fast spatial access
347
rtree->Insert( bmin, bmax, fpart );
349
// add to hashtable with equally named feature parts
350
if ( mergeLines && labelText )
352
LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find( labelText );
353
LinkedList< FeaturePart*>* lst;
354
if ( lstPtr == NULL )
356
// entry doesn't exist yet
357
lst = new LinkedList<FeaturePart*>( ptrFeaturePartCompare );
358
connectedHashtable->insertItem( labelText, lst );
360
char* txt = new char[strlen( labelText ) +1];
361
strcpy( txt, labelText );
362
connectedTexts->push_back( txt );
368
lst->push_back( fpart ); // add to the list
373
void Layer::setLabelUnit( Units label_unit )
375
if ( label_unit == PIXEL || label_unit == METER )
376
this->label_unit = label_unit;
379
Units Layer::getLabelUnit()
385
static FeaturePart* _findConnectedPart( FeaturePart* partCheck, LinkedList<FeaturePart*>* otherParts )
387
// iterate in the rest of the parts with the same label
388
Cell<FeaturePart*>* p = otherParts->getFirst();
391
if ( partCheck->isConnected( p->item ) )
393
// stop checking for other connected parts
399
return NULL; // no connected part found...
402
void Layer::joinConnectedFeatures()
404
// go through all label texts
406
while (( labelText = connectedTexts->pop_front() ) )
408
//std::cerr << "JOIN: " << labelText << std::endl;
409
LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find( labelText );
411
continue; // shouldn't happen
412
LinkedList<FeaturePart*>* parts = *partsPtr;
414
// go one-by-one part, try to merge
415
while ( parts->size() )
417
// part we'll be checking against other in this round
418
FeaturePart* partCheck = parts->pop_front();
420
FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
423
//std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
425
// remove partCheck from r-tree
426
double bmin[2], bmax[2];
427
partCheck->getBoundingBox( bmin, bmax );
428
rtree->Remove( bmin, bmax, partCheck );
430
otherPart->getBoundingBox( bmin, bmax );
432
// merge points from partCheck to p->item
433
if ( otherPart->mergeWithFeaturePart( partCheck ) )
435
// reinsert p->item to r-tree (probably not needed)
436
rtree->Remove( bmin, bmax, otherPart );
437
otherPart->getBoundingBox( bmin, bmax );
438
rtree->Insert( bmin, bmax, otherPart );
443
// we're done processing feature parts with this particular label text
449
// we're done processing connected fetures
450
delete connectedHashtable;
451
connectedHashtable = NULL;
452
delete connectedTexts;
453
connectedTexts = NULL;