~ubuntu-branches/ubuntu/wily/qgis/wily

« back to all changes in this revision

Viewing changes to src/core/pal/layer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Johan Van de Wauw
  • Date: 2010-07-11 20:23:24 UTC
  • mfrom: (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100711202324-5ktghxa7hracohmr
Tags: 1.4.0+12730-3ubuntu1
* Merge from Debian unstable (LP: #540941).
* Fix compilation issues with QT 4.7
* Add build-depends on libqt4-webkit-dev 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *   libpal - Automated Placement of Labels Library
 
3
 *
 
4
 *   Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
 
5
 *                      University of Applied Sciences, Western Switzerland
 
6
 *                      http://www.hes-so.ch
 
7
 *
 
8
 *   Contact:
 
9
 *      maxence.laurent <at> heig-vd <dot> ch
 
10
 *    or
 
11
 *      eric.taillard <at> heig-vd <dot> ch
 
12
 *
 
13
 * This file is part of libpal.
 
14
 *
 
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.
 
19
 *
 
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.
 
24
 *
 
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/>.
 
27
 *
 
28
 */
 
29
 
 
30
#ifdef HAVE_CONFIG_H
 
31
#include <config.h>
 
32
#endif
 
33
 
 
34
#define _CRT_SECURE_NO_DEPRECATE
 
35
 
 
36
#include <stddef.h>
 
37
#include <geos_c.h>
 
38
 
 
39
#include <iostream>
 
40
#include <cstring>
 
41
#include <cmath>
 
42
 
 
43
 
 
44
#include <pal/pal.h>
 
45
#include <pal/layer.h>
 
46
#include <pal/palexception.h>
 
47
#include <pal/internalexception.h>
 
48
 
 
49
#include "linkedlist.hpp"
 
50
#include "hashtable.hpp"
 
51
 
 
52
#include "feature.h"
 
53
#include "geomfunction.h"
 
54
#include "util.h"
 
55
 
 
56
#include "simplemutex.h"
 
57
 
 
58
namespace pal
 
59
{
 
60
 
 
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 )
 
66
  {
 
67
 
 
68
    this->name = new char[strlen( lyrName ) +1];
 
69
    strcpy( this->name, lyrName );
 
70
 
 
71
    modMutex = new SimpleMutex();
 
72
 
 
73
    rtree = new RTree<FeaturePart*, double, 2, double>();
 
74
    hashtable = new HashTable<Feature*> ( 5281 );
 
75
 
 
76
    connectedHashtable = new HashTable< LinkedList<FeaturePart*>* > ( 5391 );
 
77
    connectedTexts = new LinkedList< char* >( strCompare );
 
78
 
 
79
    if ( defaultPriority < 0.0001 )
 
80
      this->defaultPriority = 0.0001;
 
81
    else if ( defaultPriority > 1.0 )
 
82
      this->defaultPriority = 1.0;
 
83
    else
 
84
      this->defaultPriority = defaultPriority;
 
85
 
 
86
    featureParts = new LinkedList<FeaturePart*> ( ptrFeaturePartCompare );
 
87
    features = new LinkedList<Feature*> ( ptrFeatureCompare );
 
88
  }
 
89
 
 
90
  Layer::~Layer()
 
91
  {
 
92
    modMutex->lock();
 
93
 
 
94
    if ( featureParts )
 
95
    {
 
96
      while ( featureParts->size() )
 
97
      {
 
98
        delete featureParts->pop_front();
 
99
      }
 
100
      delete featureParts;
 
101
 
 
102
    }
 
103
 
 
104
    // this hashtable and list should be empty if they still exist
 
105
    delete connectedHashtable;
 
106
 
 
107
    // features in the hashtable
 
108
    if ( features )
 
109
    {
 
110
      while ( features->size() )
 
111
      {
 
112
        delete features->pop_front();
 
113
      }
 
114
      delete features;
 
115
    }
 
116
 
 
117
    if ( name )
 
118
      delete[] name;
 
119
 
 
120
    delete rtree;
 
121
 
 
122
    delete hashtable;
 
123
    delete modMutex;
 
124
  }
 
125
 
 
126
  Feature* Layer::getFeature( const char* geom_id )
 
127
  {
 
128
    Feature** fptr = hashtable->find( geom_id );
 
129
    return ( fptr ? *fptr : NULL );
 
130
  }
 
131
 
 
132
 
 
133
  bool Layer::isScaleValid( double scale )
 
134
  {
 
135
    return ( scale >= min_scale || min_scale == -1 )
 
136
           && ( scale <= max_scale || max_scale == -1 );
 
137
  }
 
138
 
 
139
 
 
140
  int Layer::getNbFeatures()
 
141
  {
 
142
    return features->size();
 
143
  }
 
144
 
 
145
  const char *Layer::getName()
 
146
  {
 
147
    return name;
 
148
  }
 
149
 
 
150
  Arrangement Layer::getArrangement()
 
151
  {
 
152
    return arrangement;
 
153
  }
 
154
 
 
155
  void Layer::setArrangement( Arrangement arrangement )
 
156
  {
 
157
    this->arrangement = arrangement;
 
158
  }
 
159
 
 
160
 
 
161
  bool Layer::isObstacle()
 
162
  {
 
163
    return obstacle;
 
164
  }
 
165
 
 
166
  bool Layer::isToLabel()
 
167
  {
 
168
    return toLabel;
 
169
  }
 
170
 
 
171
  bool Layer::isActive()
 
172
  {
 
173
    return active;
 
174
  }
 
175
 
 
176
 
 
177
  double Layer::getMinScale()
 
178
  {
 
179
    return min_scale;
 
180
  }
 
181
 
 
182
  double Layer::getMaxScale()
 
183
  {
 
184
    return max_scale;
 
185
  }
 
186
 
 
187
  double Layer::getPriority()
 
188
  {
 
189
    return defaultPriority;
 
190
  }
 
191
 
 
192
  void Layer::setObstacle( bool obstacle )
 
193
  {
 
194
    this->obstacle = obstacle;
 
195
  }
 
196
 
 
197
  void Layer::setActive( bool active )
 
198
  {
 
199
    this->active = active;
 
200
  }
 
201
 
 
202
  void Layer::setToLabel( bool toLabel )
 
203
  {
 
204
    this->toLabel = toLabel;
 
205
  }
 
206
 
 
207
  void Layer::setMinScale( double min_scale )
 
208
  {
 
209
    this->min_scale = min_scale;
 
210
  }
 
211
 
 
212
  void Layer::setMaxScale( double max_scale )
 
213
  {
 
214
    this->max_scale = max_scale;
 
215
  }
 
216
 
 
217
  void Layer::setPriority( double priority )
 
218
  {
 
219
    if ( priority >= 1.0 ) // low priority
 
220
      defaultPriority = 1.0;
 
221
    else if ( priority <= 0.0001 )
 
222
      defaultPriority = 0.0001; // high priority
 
223
    else
 
224
      defaultPriority = priority;
 
225
  }
 
226
 
 
227
 
 
228
 
 
229
  bool Layer::registerFeature( const char *geom_id, PalGeometry *userGeom, double label_x, double label_y, const char* labelText )
 
230
  {
 
231
    if ( !geom_id || label_x < 0 || label_y < 0 )
 
232
      return false;
 
233
 
 
234
    modMutex->lock();
 
235
 
 
236
    if ( hashtable->find( geom_id ) )
 
237
    {
 
238
      modMutex->unlock();
 
239
      throw new PalException::FeatureExists();
 
240
    }
 
241
 
 
242
    // Split MULTI GEOM and Collection in simple geometries
 
243
    GEOSGeometry *the_geom = userGeom->getGeosGeometry();
 
244
 
 
245
    Feature* f = new Feature( this, geom_id, userGeom, label_x, label_y );
 
246
 
 
247
    bool first_feat = true;
 
248
 
 
249
    double geom_size = -1, biggest_size = -1;
 
250
    FeaturePart* biggest_part = NULL;
 
251
 
 
252
    // break the (possibly multi-part) geometry into simple geometries
 
253
    LinkedList <const GEOSGeometry*> *simpleGeometries = unmulti( the_geom );
 
254
 
 
255
    while ( simpleGeometries->size() > 0 )
 
256
    {
 
257
      const GEOSGeometry* geom = simpleGeometries->pop_front();
 
258
 
 
259
      // ignore invalid geometries (e.g. polygons with self-intersecting rings)
 
260
      if ( GEOSisValid( geom ) != 1 ) // 0=invalid, 1=valid, 2=exception
 
261
      {
 
262
        std::cerr << "ignoring invalid feature " << geom_id << std::endl;
 
263
        continue;
 
264
      }
 
265
 
 
266
      int type = GEOSGeomTypeId( geom );
 
267
 
 
268
      if ( type != GEOS_POINT && type != GEOS_LINESTRING && type != GEOS_POLYGON )
 
269
        throw InternalException::UnknownGeometry();
 
270
 
 
271
      FeaturePart* fpart = new FeaturePart( f, geom );
 
272
 
 
273
      // ignore invalid geometries
 
274
      if (( type == GEOS_LINESTRING && fpart->nbPoints < 2 ) ||
 
275
          ( type == GEOS_POLYGON && fpart->nbPoints < 3 ) )
 
276
      {
 
277
        delete fpart;
 
278
        continue;
 
279
      }
 
280
 
 
281
      // polygons: reorder coordinates
 
282
      if ( type == GEOS_POLYGON && reorderPolygon( fpart->nbPoints, fpart->x, fpart->y ) != 0 )
 
283
      {
 
284
        delete fpart;
 
285
        continue;
 
286
      }
 
287
 
 
288
      if ( mode == LabelPerFeature && ( type == GEOS_POLYGON || type == GEOS_LINESTRING ) )
 
289
      {
 
290
        if ( type == GEOS_LINESTRING )
 
291
          GEOSLength( geom, &geom_size );
 
292
        else if ( type == GEOS_POLYGON )
 
293
          GEOSArea( geom, &geom_size );
 
294
 
 
295
        if ( geom_size > biggest_size )
 
296
        {
 
297
          biggest_size = geom_size;
 
298
          delete biggest_part; // safe with NULL part
 
299
          biggest_part = fpart;
 
300
        }
 
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
 
303
      }
 
304
 
 
305
      // feature part is ready!
 
306
      addFeaturePart( fpart, labelText );
 
307
 
 
308
      first_feat = false;
 
309
    }
 
310
    delete simpleGeometries;
 
311
 
 
312
    userGeom->releaseGeosGeometry( the_geom );
 
313
 
 
314
    modMutex->unlock();
 
315
 
 
316
    // if using only biggest parts...
 
317
    if ( mode == LabelPerFeature && biggest_part != NULL )
 
318
    {
 
319
      addFeaturePart( biggest_part, labelText );
 
320
      first_feat = false;
 
321
    }
 
322
 
 
323
    // add feature to layer if we have added something
 
324
    if ( !first_feat )
 
325
    {
 
326
      features->push_back( f );
 
327
      hashtable->insertItem( geom_id, f );
 
328
    }
 
329
    else
 
330
    {
 
331
      delete f;
 
332
    }
 
333
 
 
334
    return !first_feat; // true if we've added something
 
335
  }
 
336
 
 
337
  void Layer::addFeaturePart( FeaturePart* fpart, const char* labelText )
 
338
  {
 
339
    double bmin[2];
 
340
    double bmax[2];
 
341
    fpart->getBoundingBox( bmin, bmax );
 
342
 
 
343
    // add to list of layer's feature parts
 
344
    featureParts->push_back( fpart );
 
345
 
 
346
    // add to r-tree for fast spatial access
 
347
    rtree->Insert( bmin, bmax, fpart );
 
348
 
 
349
    // add to hashtable with equally named feature parts
 
350
    if ( mergeLines && labelText )
 
351
    {
 
352
      LinkedList< FeaturePart*>** lstPtr = connectedHashtable->find( labelText );
 
353
      LinkedList< FeaturePart*>* lst;
 
354
      if ( lstPtr == NULL )
 
355
      {
 
356
        // entry doesn't exist yet
 
357
        lst = new LinkedList<FeaturePart*>( ptrFeaturePartCompare );
 
358
        connectedHashtable->insertItem( labelText, lst );
 
359
 
 
360
        char* txt = new char[strlen( labelText ) +1];
 
361
        strcpy( txt, labelText );
 
362
        connectedTexts->push_back( txt );
 
363
      }
 
364
      else
 
365
      {
 
366
        lst = *lstPtr;
 
367
      }
 
368
      lst->push_back( fpart ); // add to the list
 
369
    }
 
370
  }
 
371
 
 
372
 
 
373
  void Layer::setLabelUnit( Units label_unit )
 
374
  {
 
375
    if ( label_unit == PIXEL || label_unit == METER )
 
376
      this->label_unit = label_unit;
 
377
  }
 
378
 
 
379
  Units Layer::getLabelUnit()
 
380
  {
 
381
    return label_unit;
 
382
  }
 
383
 
 
384
 
 
385
  static FeaturePart* _findConnectedPart( FeaturePart* partCheck, LinkedList<FeaturePart*>* otherParts )
 
386
  {
 
387
    // iterate in the rest of the parts with the same label
 
388
    Cell<FeaturePart*>* p = otherParts->getFirst();
 
389
    while ( p )
 
390
    {
 
391
      if ( partCheck->isConnected( p->item ) )
 
392
      {
 
393
        // stop checking for other connected parts
 
394
        return p->item;
 
395
      }
 
396
      p = p->next;
 
397
    }
 
398
 
 
399
    return NULL; // no connected part found...
 
400
  }
 
401
 
 
402
  void Layer::joinConnectedFeatures()
 
403
  {
 
404
    // go through all label texts
 
405
    char* labelText;
 
406
    while (( labelText = connectedTexts->pop_front() ) )
 
407
    {
 
408
      //std::cerr << "JOIN: " << labelText << std::endl;
 
409
      LinkedList<FeaturePart*>** partsPtr = connectedHashtable->find( labelText );
 
410
      if ( !partsPtr )
 
411
        continue; // shouldn't happen
 
412
      LinkedList<FeaturePart*>* parts = *partsPtr;
 
413
 
 
414
      // go one-by-one part, try to merge
 
415
      while ( parts->size() )
 
416
      {
 
417
        // part we'll be checking against other in this round
 
418
        FeaturePart* partCheck = parts->pop_front();
 
419
 
 
420
        FeaturePart* otherPart = _findConnectedPart( partCheck, parts );
 
421
        if ( otherPart )
 
422
        {
 
423
          //std::cerr << "- connected " << partCheck << " with " << otherPart << std::endl;
 
424
 
 
425
          // remove partCheck from r-tree
 
426
          double bmin[2], bmax[2];
 
427
          partCheck->getBoundingBox( bmin, bmax );
 
428
          rtree->Remove( bmin, bmax, partCheck );
 
429
 
 
430
          otherPart->getBoundingBox( bmin, bmax );
 
431
 
 
432
          // merge points from partCheck to p->item
 
433
          if ( otherPart->mergeWithFeaturePart( partCheck ) )
 
434
          {
 
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 );
 
439
          }
 
440
        }
 
441
      }
 
442
 
 
443
      // we're done processing feature parts with this particular label text
 
444
      delete parts;
 
445
      *partsPtr = NULL;
 
446
      delete labelText;
 
447
    }
 
448
 
 
449
    // we're done processing connected fetures
 
450
    delete connectedHashtable;
 
451
    connectedHashtable = NULL;
 
452
    delete connectedTexts;
 
453
    connectedTexts = NULL;
 
454
  }
 
455
 
 
456
 
 
457
 
 
458
} // end namespace
 
459