LCOV - code coverage report
Current view: top level - core/simplify - effectivearea.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 166 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /**********************************************************************
       2                 :            :  *
       3                 :            :  * PostGIS - Spatial Types for PostgreSQL
       4                 :            :  * http://postgis.net
       5                 :            :  *
       6                 :            :  * PostGIS is free software: you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU 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                 :            :  * PostGIS is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with PostGIS.  If not, see <http://www.gnu.org/licenses/>.
      18                 :            :  *
      19                 :            :  **********************************************************************
      20                 :            :  *
      21                 :            :  * Copyright 2014 Nicklas Avén
      22                 :            :  *
      23                 :            :  **********************************************************************/
      24                 :            : 
      25                 :            : #include "effectivearea.h"
      26                 :            : #include "qgspoint.h"
      27                 :            : 
      28                 :          0 : static MINHEAP initiate_minheap( int npoints )
      29                 :            : {
      30                 :          0 :   MINHEAP tree;
      31                 :          0 :   tree.key_array = ( areanode ** )lwalloc( npoints * sizeof( void * ) );
      32                 :          0 :   tree.maxSize = npoints;
      33                 :          0 :   tree.usedSize = 0;
      34                 :          0 :   return tree;
      35                 :            : }
      36                 :            : 
      37                 :          0 : static void destroy_minheap( MINHEAP tree )
      38                 :            : {
      39                 :          0 :   lwfree( tree.key_array );
      40                 :          0 : }
      41                 :            : 
      42                 :            : /**
      43                 :            :  * Calculate the area of a triangle in 2d
      44                 :            :  */
      45                 :          0 : static double triarea2d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint &P3 )
      46                 :            : {
      47                 :          0 :   return std::fabs( 0.5 * ( ( P1.x() - P2.x() ) * ( P3.y() - P2.y() ) - ( P1.y() - P2.y() ) * ( P3.x() - P2.x() ) ) );
      48                 :            : }
      49                 :            : 
      50                 :            : /**
      51                 :            :  * Calculate the area of a triangle in 3d space
      52                 :            :  */
      53                 :          0 : static double triarea3d( const QgsPoint &P1, const QgsPoint &P2, const QgsPoint &P3 )
      54                 :            : {
      55                 :            :   //LWDEBUG( 2, "Entered  triarea3d" );
      56                 :            :   double ax, bx, ay, by, az, bz, cx, cy, cz, area;
      57                 :            : 
      58                 :          0 :   ax = P1.x() - P2.x();
      59                 :          0 :   bx = P3.x() - P2.x();
      60                 :          0 :   ay = P1.y() - P2.y();
      61                 :          0 :   by = P3.y() - P2.y();
      62                 :          0 :   az = P1.z() - P2.z();
      63                 :          0 :   bz = P3.z() - P2.z();
      64                 :            : 
      65                 :          0 :   cx = ay * bz - az * by;
      66                 :          0 :   cy = az * bx - ax * bz;
      67                 :          0 :   cz = ax * by - ay * bx;
      68                 :            : 
      69                 :          0 :   area = std::fabs( 0.5 * ( std::sqrt( cx * cx + cy * cy + cz * cz ) ) );
      70                 :          0 :   return area;
      71                 :            : }
      72                 :            : 
      73                 :            : /**
      74                 :            :  * We create the minheap by ordering the minheap array by the areas in the areanode structs that the minheap keys refer to
      75                 :            :  */
      76                 :          0 : static int cmpfunc( const void *a, const void *b )
      77                 :            : {
      78                 :          0 :   double v1 = ( *( areanode ** )a )->area;
      79                 :          0 :   double v2 = ( *( areanode ** )b )->area;
      80                 :            : 
      81                 :            :   /* qsort gives unpredictable results when comparing identical values.
      82                 :            :    * If two values are the same we force returning the last point in the point array.
      83                 :            :    * That way we get the same ordering on different machines and platforms
      84                 :            :    */
      85                 :          0 :   if ( v1 == v2 )
      86                 :          0 :     return ( *( areanode ** )a ) - ( *( areanode ** )b );
      87                 :            :   else
      88                 :          0 :     return ( v1 > v2 ) ? 1 : -1;
      89                 :          0 : }
      90                 :            : 
      91                 :            : /**
      92                 :            :  * Sift Down
      93                 :            :  */
      94                 :          0 : static void down( MINHEAP *tree, areanode *arealist, int parent )
      95                 :            : {
      96                 :            :   //LWDEBUG( 2, "Entered  down" );
      97                 :          0 :   areanode **treearray = tree->key_array;
      98                 :          0 :   int left = parent * 2 + 1;
      99                 :          0 :   int right = left + 1;
     100                 :          0 :   areanode *tmp = nullptr;
     101                 :          0 :   int swap = parent;
     102                 :          0 :   double leftarea = 0;
     103                 :          0 :   double rightarea = 0;
     104                 :          0 :   double parentarea = ( ( areanode * )treearray[parent] )->area;
     105                 :            : 
     106                 :          0 :   if ( left < tree->usedSize )
     107                 :            :   {
     108                 :          0 :     leftarea = ( ( areanode * )treearray[left] )->area;
     109                 :          0 :     if ( parentarea > leftarea ) swap = left;
     110                 :          0 :   }
     111                 :          0 :   if ( right < tree->usedSize )
     112                 :            :   {
     113                 :          0 :     rightarea = ( ( areanode * )treearray[right] )->area;
     114                 :          0 :     if ( rightarea < parentarea && rightarea < leftarea ) swap = right;
     115                 :          0 :   }
     116                 :          0 :   if ( swap > parent )
     117                 :            :   {
     118                 :            :     // OK, we have to swap something
     119                 :          0 :     tmp = treearray[parent];
     120                 :          0 :     treearray[parent] = treearray[swap];
     121                 :            :     // Update reference
     122                 :          0 :     ( ( areanode * )treearray[parent] )->treeindex = parent;
     123                 :          0 :     treearray[swap] = tmp;
     124                 :            :     // Update reference
     125                 :          0 :     ( ( areanode * )treearray[swap] )->treeindex = swap;
     126                 :          0 :     if ( swap < tree->usedSize ) down( tree, arealist, swap );
     127                 :          0 :   }
     128                 :          0 : }
     129                 :            : 
     130                 :            : /**
     131                 :            :  * Sift Up
     132                 :            :  */
     133                 :          0 : static void up( MINHEAP *tree, areanode *arealist, int c )
     134                 :            : {
     135                 :            :   //LWDEBUG( 2, "Entered  up" );
     136                 :          0 :   areanode *tmp = nullptr;
     137                 :            : 
     138                 :            :   Q_UNUSED( arealist )
     139                 :            : 
     140                 :          0 :   areanode **treearray = tree->key_array;
     141                 :          0 :   int parent = ( c - 1 ) / 2;
     142                 :            : 
     143                 :          0 :   while ( ( ( areanode * )treearray[c] )->area < ( ( areanode * )treearray[parent] )->area )
     144                 :            :   {
     145                 :            :     // OK, we have to swap
     146                 :          0 :     tmp = treearray[parent];
     147                 :          0 :     treearray[parent] = treearray[c];
     148                 :            :     // Update reference
     149                 :          0 :     ( ( areanode * )treearray[parent] )->treeindex = parent;
     150                 :          0 :     treearray[c] = tmp;
     151                 :            :     // Update reference
     152                 :          0 :     ( ( areanode * )treearray[c] )->treeindex = c;
     153                 :          0 :     c = parent;
     154                 :          0 :     parent = ( c - 1 ) / 2;
     155                 :            :   }
     156                 :          0 : }
     157                 :            : 
     158                 :            : /**
     159                 :            :  * Gets a reference to the point with the smallest effective area from the root of the min heap
     160                 :            :  */
     161                 :          0 : static areanode *minheap_pop( MINHEAP *tree, areanode *arealist )
     162                 :            : {
     163                 :            :   //LWDEBUG( 2, "Entered  minheap_pop" );
     164                 :          0 :   areanode *res = tree->key_array[0];
     165                 :            : 
     166                 :            :   // put last value first
     167                 :          0 :   tree->key_array[0] = tree->key_array[( tree->usedSize ) - 1];
     168                 :          0 :   ( ( areanode * )tree->key_array[0] )->treeindex = 0;
     169                 :            : 
     170                 :          0 :   tree->usedSize--;
     171                 :          0 :   down( tree, arealist, 0 );
     172                 :          0 :   return res;
     173                 :            : }
     174                 :            : 
     175                 :            : /**
     176                 :            :  * The member of the minheap at index idx is changed. Update the tree and make restore the heap property
     177                 :            :  */
     178                 :          0 : static void minheap_update( MINHEAP *tree, areanode *arealist, int idx )
     179                 :            : {
     180                 :          0 :   areanode **treearray = tree->key_array;
     181                 :          0 :   int parent = ( idx - 1 ) / 2;
     182                 :            : 
     183                 :          0 :   if ( ( ( areanode * )treearray[idx] )->area < ( ( areanode * )treearray[parent] )->area )
     184                 :          0 :     up( tree, arealist, idx );
     185                 :            :   else
     186                 :          0 :     down( tree, arealist, idx );
     187                 :          0 : }
     188                 :            : 
     189                 :            : /**
     190                 :            :  * To get the effective area, we have to check what area a point results in when all smaller areas are eliminated
     191                 :            :  */
     192                 :          0 : static void tune_areas( EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld )
     193                 :            : {
     194                 :            :   //LWDEBUG( 2, "Entered  tune_areas" );
     195                 :          0 :   QgsPoint P1;
     196                 :          0 :   QgsPoint P2;
     197                 :          0 :   QgsPoint P3;
     198                 :            :   double area;
     199                 :          0 :   int go_on = 1;
     200                 :          0 :   double check_order_min_area = 0;
     201                 :            : 
     202                 :          0 :   int npoints = ea->inpts.size();
     203                 :            :   int i;
     204                 :            :   int current, before_current, after_current;
     205                 :            : 
     206                 :          0 :   MINHEAP tree = initiate_minheap( npoints );
     207                 :            : 
     208                 :            :   // Add all keys (index in initial_arealist) into minheap array
     209                 :          0 :   for ( i = 0; i < npoints; i++ )
     210                 :            :   {
     211                 :          0 :     tree.key_array[i] = ea->initial_arealist + i;
     212                 :            :     //LWDEBUGF( 2, "add nr %d, with area %lf, and %lf", i, ea->initial_arealist[i].area, tree.key_array[i]->area );
     213                 :          0 :   }
     214                 :          0 :   tree.usedSize = npoints;
     215                 :            : 
     216                 :            :   // order the keys by area, small to big
     217                 :          0 :   qsort( tree.key_array, npoints, sizeof( void * ), cmpfunc );
     218                 :            : 
     219                 :            :   // We have to put references to our tree in our point-list
     220                 :          0 :   for ( i = 0; i < npoints; i++ )
     221                 :            :   {
     222                 :          0 :     ( ( areanode * )tree.key_array[i] )->treeindex = i;
     223                 :            :     //LWDEBUGF( 4, "Check ordering qsort gives, area=%lf and belong to point %d", (( areanode* )tree.key_array[i] )->area, tree.key_array[i] - ea->initial_arealist );
     224                 :          0 :   }
     225                 :            : 
     226                 :            :   // OK, now we have a minHeap, just need to keep it
     227                 :          0 :   i = 0;
     228                 :          0 :   while ( go_on )
     229                 :            :   {
     230                 :            :     // Get a reference to the point with the currently smallest effective area
     231                 :          0 :     current = minheap_pop( &tree, ea->initial_arealist ) - ea->initial_arealist;
     232                 :            : 
     233                 :            :     // We have found the smallest area. That is the resulting effective area for the "current" point
     234                 :          0 :     if ( i < npoints - avoid_collaps )
     235                 :          0 :       ea->res_arealist[current] = ea->initial_arealist[current].area;
     236                 :            :     else
     237                 :          0 :       ea->res_arealist[current] = FLT_MAX;
     238                 :            : 
     239                 :          0 :     if ( ea->res_arealist[current] < check_order_min_area )
     240                 :          0 :       lwerror( "Oh no, this is a bug. For some reason the minHeap returned our points in the wrong order. Please file a ticket in PostGIS ticket system, or send a mail at the mailing list. Returned area = %lf, and last area = %lf", ea->res_arealist[current], check_order_min_area );
     241                 :            : 
     242                 :          0 :     check_order_min_area = ea->res_arealist[current];
     243                 :            : 
     244                 :            :     // The found smallest area point is now regarded as eliminated and we have to recalculate the area the adjacent (ignoring earlier eliminated points) points gives
     245                 :            : 
     246                 :            :     // Find point before and after
     247                 :          0 :     before_current = ea->initial_arealist[current].prev;
     248                 :          0 :     after_current  = ea->initial_arealist[current].next;
     249                 :            : 
     250                 :          0 :     P2 = ea->inpts.at( before_current );
     251                 :          0 :     P3 = ea->inpts.at( after_current );
     252                 :            : 
     253                 :            :     // Check if point before current point is the first in the point array.
     254                 :          0 :     if ( before_current > 0 )
     255                 :            :     {
     256                 :          0 :       P1 = ea->inpts.at( ea->initial_arealist[before_current].prev );
     257                 :            : 
     258                 :          0 :       if ( ea->is3d )
     259                 :          0 :         area = triarea3d( P1, P2, P3 );
     260                 :            :       else
     261                 :          0 :         area = triarea2d( P1, P2, P3 );
     262                 :            : 
     263                 :          0 :       ea->initial_arealist[before_current].area = FP_MAX( area, ea->res_arealist[current] );
     264                 :          0 :       minheap_update( &tree, ea->initial_arealist, ea->initial_arealist[before_current].treeindex );
     265                 :          0 :     }
     266                 :          0 :     if ( after_current < npoints - 1 ) // Check if point after current point is the last in the point array.
     267                 :            :     {
     268                 :          0 :       P1 = P2;
     269                 :          0 :       P2 = P3;
     270                 :          0 :       P3 = ea->inpts.at( ea->initial_arealist[after_current].next );
     271                 :            : 
     272                 :          0 :       if ( ea->is3d )
     273                 :          0 :         area = triarea3d( P1, P2, P3 );
     274                 :            :       else
     275                 :          0 :         area = triarea2d( P1, P2, P3 );
     276                 :            : 
     277                 :          0 :       ea->initial_arealist[after_current].area = FP_MAX( area, ea->res_arealist[current] );
     278                 :          0 :       minheap_update( &tree, ea->initial_arealist, ea->initial_arealist[after_current].treeindex );
     279                 :          0 :     }
     280                 :            : 
     281                 :            :     // rearrange the nodes so the eliminated point will be ignored on the next run
     282                 :          0 :     ea->initial_arealist[before_current].next = ea->initial_arealist[current].next;
     283                 :          0 :     ea->initial_arealist[after_current ].prev = ea->initial_arealist[current].prev;
     284                 :            : 
     285                 :            :     // Check if we are finished
     286                 :          0 :     if ( ( !set_area && ea->res_arealist[current] > trshld ) || ( ea->initial_arealist[0].next == ( npoints - 1 ) ) )
     287                 :          0 :       go_on = 0;
     288                 :            : 
     289                 :          0 :     i++;
     290                 :            :   }
     291                 :          0 :   destroy_minheap( tree );
     292                 :          0 : }
     293                 :            : 
     294                 :            : /**
     295                 :            :  * We calculate the effective area for the first time
     296                 :            :  */
     297                 :          0 : void ptarray_calc_areas( EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld )
     298                 :            : {
     299                 :            :   //LWDEBUG( 2, "Entered  ptarray_calc_areas" );
     300                 :            :   int i;
     301                 :          0 :   int npoints = ea->inpts.size();
     302                 :            :   double area;
     303                 :            : 
     304                 :          0 :   QgsPoint P1;
     305                 :          0 :   QgsPoint P2;
     306                 :          0 :   QgsPoint P3;
     307                 :          0 :   P1 = ea->inpts.at( 0 );
     308                 :          0 :   P2 = ea->inpts.at( 1 );
     309                 :            : 
     310                 :            :   // The first and last point shall always have the maximum effective area. We use float max to not make trouble for bbox
     311                 :          0 :   ea->initial_arealist[0].area = ea->initial_arealist[npoints - 1].area = FLT_MAX;
     312                 :          0 :   ea->res_arealist[0] = ea->res_arealist[npoints - 1] = FLT_MAX;
     313                 :            : 
     314                 :          0 :   ea->initial_arealist[0].next = 1;
     315                 :          0 :   ea->initial_arealist[0].prev = 0;
     316                 :            : 
     317                 :          0 :   for ( i = 1; i < ( npoints ) - 1; i++ )
     318                 :            :   {
     319                 :          0 :     ea->initial_arealist[i].next = i + 1;
     320                 :          0 :     ea->initial_arealist[i].prev = i - 1;
     321                 :          0 :     P3 = ea->inpts.at( i + 1 );
     322                 :            : 
     323                 :          0 :     if ( ea->is3d )
     324                 :          0 :       area = triarea3d( P1, P2, P3 );
     325                 :            :     else
     326                 :          0 :       area = triarea2d( P1, P2, P3 );
     327                 :            : 
     328                 :            :     //LWDEBUGF( 4, "Write area %lf to point %d on address %p", area, i, &( ea->initial_arealist[i].area ) );
     329                 :          0 :     ea->initial_arealist[i].area = area;
     330                 :          0 :     P1 = P2;
     331                 :          0 :     P2 = P3;
     332                 :          0 :   }
     333                 :          0 :   ea->initial_arealist[npoints - 1].next = npoints - 1;
     334                 :          0 :   ea->initial_arealist[npoints - 1].prev = npoints - 2;
     335                 :            : 
     336                 :          0 :   for ( i = 1; i < ( npoints ) - 1; i++ )
     337                 :            :   {
     338                 :          0 :     ea->res_arealist[i] = FLT_MAX;
     339                 :          0 :   }
     340                 :          0 :   tune_areas( ea, avoid_collaps, set_area, trshld );
     341                 :          0 : }

Generated by: LCOV version 1.14