1 /******************************************************************************
2 * LibGHT, software to manage point clouds.
3 * LibGHT is free and open source software provided by the Government of Canada
4 * Copyright (c) 2012 Natural Resources Canada
5 *
6 * Nouri Sabo <nsabo@NRCan.gc.ca>, Natural Resources Canada
7 * Paul Ramsey <pramsey@opengeo.org>, OpenGeo
8 *
9 ******************************************************************************/
10
11 #include "ght_internal.h"
12 #include <float.h>
13
14 /******************************************************************************
15 * GhtNodeList
16 ******************************************************************************/
17
18 /** New, empty, nodelist */
19 GhtErr
ght_nodelist_new(int capacity,GhtNodeList ** nodelist)20 ght_nodelist_new(int capacity, GhtNodeList **nodelist)
21 {
22 GhtNodeList *nl;
23 assert(nodelist);
24 nl = ght_malloc(sizeof(GhtNodeList));
25 if ( ! nl ) return GHT_ERROR;
26 memset(nl, 0, sizeof(GhtNodeList));
27 nl->nodes = NULL;
28 nl->num_nodes = 0;
29 nl->max_nodes = capacity;
30 if ( capacity )
31 {
32 nl->nodes = ght_malloc(sizeof(GhtNode*)*capacity);
33 }
34 *nodelist = nl;
35 return GHT_OK;
36 }
37
38 GhtErr
ght_nodelist_get_num_nodes(const GhtNodeList * nodelist,int * num_nodes)39 ght_nodelist_get_num_nodes(const GhtNodeList *nodelist, int *num_nodes)
40 {
41 assert(nodelist);
42 *num_nodes = nodelist->num_nodes;
43 return GHT_OK;
44 }
45
46 GhtErr
ght_nodelist_get_node(const GhtNodeList * nodelist,int index,GhtNode ** node)47 ght_nodelist_get_node(const GhtNodeList *nodelist, int index, GhtNode **node)
48 {
49 assert(nodelist);
50 assert(nodelist->num_nodes > index);
51 assert(index >= 0);
52
53 *node = nodelist->nodes[index];
54 return GHT_OK;
55 }
56
57 /** Free just the containing structure, leaving the nodes */
58 GhtErr
ght_nodelist_free_shallow(GhtNodeList * nl)59 ght_nodelist_free_shallow(GhtNodeList *nl)
60 {
61 if ( nl->nodes )
62 ght_free(nl->nodes);
63
64 ght_free(nl);
65 }
66
67 /** Free all the nodes, then the containing stuff */
68 GhtErr
ght_nodelist_free_deep(GhtNodeList * nl)69 ght_nodelist_free_deep(GhtNodeList *nl)
70 {
71 int i;
72 if ( nl->nodes )
73 {
74 for ( i = 0; i < nl->num_nodes; i++ )
75 {
76 if ( nl->nodes[i] )
77 ght_node_free(nl->nodes[i]);
78 }
79 }
80 return ght_nodelist_free_shallow(nl);
81 }
82
83 /** Add node, adding memory space as necessary. */
84 GhtErr
ght_nodelist_add_node(GhtNodeList * nl,GhtNode * node)85 ght_nodelist_add_node(GhtNodeList *nl, GhtNode *node)
86 {
87
88 /* First time, initialize */
89 if ( nl->max_nodes == 0 )
90 {
91 nl->nodes = ght_malloc(sizeof(GhtNode*) * 8);
92 nl->max_nodes = 8;
93 }
94
95 /* Node list is full, so expand it */
96 if ( nl->num_nodes == nl->max_nodes )
97 {
98 nl->max_nodes *= 2;
99 nl->nodes = ght_realloc(nl->nodes, sizeof(GhtNode*) * nl->max_nodes);
100 if ( ! nl->nodes ) return GHT_ERROR;
101 }
102
103 /* Something wrong with memory? */
104 if ( ! nl->nodes ) return GHT_ERROR;
105
106 /* Add the node to list */
107 nl->nodes[nl->num_nodes] = node;
108 nl->num_nodes++;
109 return GHT_OK;
110 }
111
112
113
114 /******************************************************************************
115 * GhtNode
116 ******************************************************************************/
117
118 /** Some nodelist utility functions */
119 static int
ght_node_is_leaf(const GhtNode * node)120 ght_node_is_leaf(const GhtNode *node)
121 {
122 return (! node->children) || (node->children->num_nodes == 0);
123 }
124
125 static int
ght_node_num_children(const GhtNode * node)126 ght_node_num_children(const GhtNode *node)
127 {
128 if ( ! node->children)
129 return 0;
130 return node->children->num_nodes;
131 }
132
133 static GhtErr
ght_node_new(GhtNode ** node)134 ght_node_new(GhtNode **node)
135 {
136 GhtNode *n = ght_malloc(sizeof(GhtNode));
137 if ( ! n ) return GHT_ERROR;
138 memset(n, 0, sizeof(GhtNode));
139 n->children = NULL;
140 n->attributes = NULL;
141 n->hash = NULL;
142 *node = n;
143 return GHT_OK;
144 }
145
146 GhtErr
ght_node_set_hash(GhtNode * node,GhtHash * hash)147 ght_node_set_hash(GhtNode *node, GhtHash *hash)
148 {
149 if ( node->hash )
150 ght_free(node->hash);
151 node->hash = hash;
152 return GHT_OK;
153 }
154
155
156 GhtErr
ght_node_get_attributes(const GhtNode * node,GhtAttribute ** attr)157 ght_node_get_attributes(const GhtNode *node, GhtAttribute **attr)
158 {
159 if ( node->attributes )
160 {
161 *attr = node->attributes;
162 return GHT_OK;
163 }
164 *attr = NULL;
165 return GHT_ERROR;
166 }
167
168 GhtErr
ght_node_get_coordinate(const GhtNode * node,GhtCoordinate * coord)169 ght_node_get_coordinate(const GhtNode *node, GhtCoordinate *coord)
170 {
171 if ( ! node->hash )
172 {
173 return GHT_ERROR;
174 }
175 return ght_coordinate_from_hash(node->hash, coord);
176 }
177
178 /** Create new node, taking ownership of hash parameter */
179 GhtErr
ght_node_new_from_hash(GhtHash * hash,GhtNode ** node)180 ght_node_new_from_hash(GhtHash *hash, GhtNode **node)
181 {
182 GHT_TRY(ght_node_new(node));
183 GHT_TRY(ght_node_set_hash(*node, ght_strdup(hash)));
184 return GHT_OK;
185 }
186
187 /** Create new node, taking ownership of hash parameter */
188 GhtErr
ght_node_new_from_coordinate(const GhtCoordinate * coord,unsigned int resolution,GhtNode ** node)189 ght_node_new_from_coordinate(const GhtCoordinate *coord, unsigned int resolution, GhtNode **node)
190 {
191 GhtHash *hash;
192 assert(node != NULL);
193 assert(coord != NULL);
194 GHT_TRY(ght_hash_from_coordinate(coord, resolution, &hash));
195 GHT_TRY(ght_node_new(node));
196 GHT_TRY(ght_node_set_hash(*node, hash));
197 return GHT_OK;
198 }
199
200 static GhtErr
ght_node_add_child(GhtNode * parent,GhtNode * child)201 ght_node_add_child(GhtNode *parent, GhtNode *child)
202 {
203 if ( ! parent->children )
204 {
205 ght_nodelist_new(1, &(parent->children));
206 }
207 return ght_nodelist_add_node(parent->children, child);
208 }
209
210 static GhtErr
ght_node_transfer_attributes(GhtNode * node_from,GhtNode * node_to)211 ght_node_transfer_attributes(GhtNode *node_from, GhtNode *node_to)
212 {
213 size_t sz;
214
215 /* Nothing to transfer */
216 if ( ! node_from->attributes )
217 return GHT_OK;
218
219 /* Uh oh, something is already there */
220 if ( node_to->attributes )
221 return GHT_ERROR;
222
223 node_to->attributes = node_from->attributes;
224 node_from->attributes = NULL;
225
226 return GHT_OK;
227 }
228
229 /**
230 * Recursive function, walk down from parent node, looking for
231 * appropriate insertion point for node_to_insert. If duplicates,
232 * and duplicate leaf, insert as hash-less "attribute only" node.
233 * ["abcdefg", "abcdeff", "abcdddd", "abbbeee"] becomes
234 * "ab"->["c"->["d"->["ddd","ef"->["g","f"]]],"b"]
235 */
236 GhtErr
ght_node_insert_node(GhtNode * node,GhtNode * node_to_insert,GhtDuplicates duplicates)237 ght_node_insert_node(GhtNode *node, GhtNode *node_to_insert, GhtDuplicates duplicates)
238 {
239 GhtHash *node_leaf, *node_to_insert_leaf;
240 GhtErr err;
241 GhtHashMatch matchtype;
242
243 /* NULL hash implies this node is a faux node for duplicate points */
244 if ( ! node->hash )
245 return GHT_INCOMPLETE;
246
247 /* matchtype in (GHT_NONE, GHT_GLOBAL, GHT_SAME, GHT_CHILD, GHT_SPLIT) */
248 /* NONE and GLOBAL come back with GHT_ERROR, so we don't handle them yet */
249 GHT_TRY(ght_hash_leaf_parts(node->hash, node_to_insert->hash, GHT_MAX_HASH_LENGTH,
250 &matchtype, &node_leaf, &node_to_insert_leaf));
251
252 /* Insert node is child of node, either explicitly, or implicitly for */
253 /* the "" hash which serves as a master parent */
254 /* "abcdef" is a GHT_CHILD or "abc", and gets added as "def" */
255 if ( matchtype == GHT_CHILD || matchtype == GHT_GLOBAL )
256 {
257 int i;
258 ght_node_set_hash(node_to_insert, ght_strdup(node_to_insert_leaf));
259 for ( i = 0; i < ght_node_num_children(node); i++ )
260 {
261 err = ght_node_insert_node(node->children->nodes[i], node_to_insert, duplicates);
262 /* Node added to one of the children */
263 if ( err == GHT_OK ) return GHT_OK;
264 }
265 /* Node didn't fit any of the children, so add it at this level */
266 return ght_node_add_child(node, node_to_insert);
267 }
268
269 if ( matchtype == GHT_SAME )
270 {
271 /* New node is duplicate of this node. We insert an */
272 /* empty node (no hash) underneath, to hang attributes off of */
273 /* and use this node as the parent */
274 if ( duplicates )
275 {
276 /* If this is the first duplicate, add a copy of the parent */
277 /* To serve as a proxy leaf for this value */
278 if ( ( ! node->children ) || ( node->children->num_nodes == 0 ) )
279 {
280 GhtNode *parent_leaf;
281 GHT_TRY(ght_node_new(&parent_leaf));
282 GHT_TRY(ght_node_transfer_attributes(node, parent_leaf));
283 GHT_TRY(ght_node_add_child(node, parent_leaf));
284 }
285
286 /* Add the new node under the parent, stripping the hash */
287 ght_free(node_to_insert->hash);
288 node_to_insert->hash = NULL;
289 GHT_TRY(ght_node_add_child(node, node_to_insert));
290
291 return GHT_OK;
292 }
293 else
294 {
295 /* For now, we just skip duplicates. */
296 /* In future, average / median the duplicates onto parent here? */
297 return GHT_OK;
298 }
299 }
300
301 /* "abcdef" and "abcghi" need to GHT_SPLIT, into "abc"->["def", "ghi"] */
302 if ( matchtype == GHT_SPLIT )
303 {
304 /* We need a new node to hold that part of the parent that is not shared */
305 GhtNode *another_node_to_insert;
306 GHT_TRY(ght_node_new_from_hash(node_leaf, &another_node_to_insert));
307 /* Move attributes to the new child */
308 GHT_TRY(ght_node_transfer_attributes(node, another_node_to_insert));
309
310 /* Any children of the parent need to move down the tree with the unique part of the hash */
311 if ( node->children )
312 {
313 another_node_to_insert->children = node->children;
314 node->children = NULL;
315 }
316 /* Null-terminate parent hash at end of shared part */
317 *node_leaf = '\0';
318 /* Pull the non-shared part of insert node hash to the front */
319 memmove(node_to_insert->hash, node_to_insert_leaf, strlen(node_to_insert_leaf)+1);
320 /* Add the unique portion of the parent to the parent */
321 GHT_TRY(ght_node_add_child(node, another_node_to_insert));
322 /* Add the unique portion of the insert node to the parent */
323 GHT_TRY(ght_node_add_child(node, node_to_insert));
324 /* Done! */
325 return GHT_OK;
326 }
327
328 /* Don't get here */
329 return GHT_ERROR;
330
331 }
332
333
334 GhtErr
ght_node_to_string(GhtNode * node,stringbuffer_t * sb,int level)335 ght_node_to_string(GhtNode *node, stringbuffer_t *sb, int level)
336 {
337 int i = 0;
338
339 /* Print hash */
340 if ( node->hash )
341 ght_stringbuffer_aprintf(sb, "%*s%s", 2*level, "", node->hash);
342 else
343 ght_stringbuffer_aprintf(sb, "%*s%s", 2*level, "", "[hash-is-null]");
344
345 /* Print attributes */
346 if ( node->attributes )
347 {
348 GhtAttribute *attr = node->attributes;
349 ght_stringbuffer_append(sb, " ");
350 while ( attr )
351 {
352 ght_attribute_to_string(attr, sb);
353 if ( attr->next ) ght_stringbuffer_append(sb, ":");
354 attr = attr->next;
355 }
356 }
357 ght_stringbuffer_append(sb, "\n");
358
359 /* Recurse into children */
360 for ( i = 0; i < ght_node_num_children(node); i++ )
361 {
362 GHT_TRY(ght_node_to_string(node->children->nodes[i], sb, level + 1));
363 }
364 return GHT_OK;
365 }
366
367
368 GhtErr
ght_node_count_leaves(const GhtNode * node,int * count)369 ght_node_count_leaves(const GhtNode *node, int *count)
370 {
371 int i;
372 GhtErr err;
373
374 /* No-op on empty */
375 if ( ! node ) return GHT_OK;
376
377 if ( ght_node_is_leaf(node) )
378 {
379 *count += 1;
380 return GHT_OK;
381 }
382 for ( i = 0; i < ght_node_num_children(node); i++ )
383 {
384 GHT_TRY(ght_node_count_leaves(node->children->nodes[i], count));
385 }
386 return GHT_OK;
387 }
388
389 GhtErr
ght_node_free(GhtNode * node)390 ght_node_free(GhtNode *node)
391 {
392 int i;
393 const int deep = 1;
394 assert(node != NULL);
395
396 if ( node->attributes )
397 GHT_TRY(ght_attribute_free(node->attributes));
398
399 if ( node->children )
400 GHT_TRY(ght_nodelist_free_deep(node->children));
401
402 if ( node->hash )
403 GHT_TRY(ght_hash_free(node->hash));
404
405 ght_free(node);
406 }
407
408
409 GhtErr
ght_node_add_attribute(GhtNode * node,GhtAttribute * attribute)410 ght_node_add_attribute(GhtNode *node, GhtAttribute *attribute)
411 {
412 GhtAttribute *attr = node->attributes;
413
414 /* First one! */
415 if ( ! node->attributes )
416 {
417 node->attributes = attribute;
418 return GHT_OK;
419 }
420
421 while ( attr->next )
422 {
423 attr = attr->next;
424 }
425
426 attr->next = attribute;
427 return GHT_OK;
428 }
429
430 GhtErr
ght_node_count_attributes(const GhtNode * node,uint8_t * count)431 ght_node_count_attributes(const GhtNode *node, uint8_t *count)
432 {
433 uint8_t c = 0;
434 GhtAttribute *attr = node->attributes;
435 while ( attr )
436 {
437 c++;
438 attr = attr->next;
439 }
440 *count = c;
441 return GHT_OK;
442 }
443
444 GhtErr
ght_node_delete_attribute(GhtNode * node,const GhtDimension * dim)445 ght_node_delete_attribute(GhtNode *node, const GhtDimension *dim)
446 {
447 GhtAttribute *attr = node->attributes;
448 GhtAttribute *attr_prev;
449
450 /* No attributes, noop */
451 if ( ! attr )
452 return GHT_OK;
453
454 /* Roll forward until we find a match */
455 while( attr->dim != dim )
456 {
457 if ( attr->next )
458 {
459 attr_prev = attr;
460 attr = attr->next;
461 }
462 /* No attributes matches dimension */
463 else
464 {
465 return GHT_ERROR;
466 }
467 }
468
469 /* Handle first attribute in list specially */
470 if ( attr == node->attributes )
471 {
472 if ( attr->next )
473 node->attributes = attr->next;
474 else
475 node->attributes = NULL;
476 }
477 else
478 {
479 attr_prev->next = attr->next;
480 }
481
482 ght_free(attr);
483 return GHT_OK;
484 }
485
486 /*
487 * Recursive compaction routine. Pulls attribute up to the highest node such that
488 * all children share the attribute value.
489 */
490 static GhtErr
ght_node_compact_attribute_with_delta(GhtNode * node,const GhtDimension * dim,double delta,GhtAttribute * compacted_attribute)491 ght_node_compact_attribute_with_delta(GhtNode *node,
492 const GhtDimension *dim,
493 double delta,
494 GhtAttribute *compacted_attribute)
495 {
496 int i;
497
498 /* This is an internal node, see if all the children share a value in this dimension */
499 if ( node->children && node->children->num_nodes > 0 )
500 {
501 double minval = DBL_MAX;
502 double maxval = -1 * DBL_MAX;
503 double totval = 0.0;
504 int node_count = 0;
505
506 /* Figure out the range of values for this dimension in child nodes */
507 for ( i = 0; i < node->children->num_nodes; i++ )
508 {
509 GhtAttribute attr;
510 GhtErr err;
511 err = ght_node_compact_attribute_with_delta(node->children->nodes[i], dim, delta, &attr);
512 if ( err == GHT_OK )
513 {
514 double d;
515 GHT_TRY(ght_attribute_get_value(&attr, &d));
516 (d < minval) ? (minval = d) : 0;
517 (d > maxval) ? (maxval = d) : 0;
518 totval += d;
519 node_count++;
520 }
521 else
522 {
523 continue;
524 }
525 }
526
527 /* If the range is narrow, and we got values from all our children, compact them */
528 if ( (maxval-minval) < delta && node_count == node->children->num_nodes )
529 {
530 double val = (minval+maxval)/2.0;
531 GhtAttribute *myattr;
532 for ( i = 0; i < node->children->num_nodes; i++ )
533 {
534 ght_node_delete_attribute(node->children->nodes[i], dim);
535 }
536 ght_attribute_new_from_double(dim, val, &myattr);
537 memcpy(compacted_attribute, myattr, sizeof(GhtAttribute));
538 ght_node_add_attribute(node, myattr);
539 return GHT_OK;
540 }
541 return GHT_ERROR;
542 }
543 /* This is a leaf node, send the attribute value up to the caller */
544 else
545 {
546 if ( ! node->attributes ) return GHT_ERROR;
547 return ght_attribute_get_by_dimension(node->attributes, dim, compacted_attribute);
548 }
549 }
550
551 GhtErr
ght_node_compact_attribute(GhtNode * node,const GhtDimension * dim,GhtAttribute * attr)552 ght_node_compact_attribute(GhtNode *node, const GhtDimension *dim, GhtAttribute *attr)
553 {
554 return ght_node_compact_attribute_with_delta(node, dim, 10e-8, attr);
555 }
556
557 /**
558 * Recursive node serialization:
559 * - length of GhtHash
560 * - GhtHash (no null terminator)
561 * - number of GhtAttributes
562 * - GhtAttribute[]
563 * - number of child GhtNodes
564 * - GhtNode[]
565 */
566 GhtErr
ght_node_write(const GhtNode * node,GhtWriter * writer)567 ght_node_write(const GhtNode *node, GhtWriter *writer)
568 {
569 uint8_t attrcount = 0;
570 uint8_t childcount = 0;
571 GhtAttribute *attr = node->attributes;
572
573 /* Write the hash */
574 GHT_TRY(ght_hash_write(node->hash, writer));
575
576 /* Write the attributes */
577 GHT_TRY(ght_node_count_attributes(node, &attrcount));
578 ght_write(writer, &attrcount, 1);
579 while( attr )
580 {
581 ght_attribute_write(attr, writer);
582 attr = attr->next;
583 }
584
585 /* Write the children */
586 if ( node->children )
587 childcount = node->children->num_nodes;
588
589 ght_write(writer, &childcount, 1);
590 if ( childcount )
591 {
592 int i;
593 for ( i = 0; i < node->children->num_nodes; i++ )
594 {
595 ght_node_write(node->children->nodes[i], writer);
596 }
597 }
598 return GHT_OK;
599 }
600
601 /**
602 * Recursive node deserialization
603 */
604 GhtErr
ght_node_read(GhtReader * reader,GhtNode ** node)605 ght_node_read(GhtReader *reader, GhtNode **node)
606 {
607 int i;
608 uint8_t attrcount;
609 uint8_t childcount;
610 GhtHash *hash = NULL;
611 GhtNode *n = NULL;
612 GhtAttribute *attr = NULL;
613
614 /* Read the hash string */
615 ght_hash_read(reader, &hash);
616 if ( hash )
617 {
618 GHT_TRY(ght_node_new_from_hash(hash, &n));
619 }
620 else
621 {
622 GHT_TRY(ght_node_new(&n));
623 }
624
625 /* Read the attributes */
626 ght_read(reader, &attrcount, 1);
627 while ( attrcount )
628 {
629 GHT_TRY(ght_attribute_read(reader, &attr));
630 GHT_TRY(ght_node_add_attribute(n, attr));
631 attrcount--;
632 }
633
634 /* Read the children */
635 ght_read(reader, &childcount, 1);
636 /* Set up an exactly sized node list to hold the children */
637 if ( childcount > 0 )
638 {
639 GHT_TRY(ght_nodelist_new(childcount, &(n->children)));
640 }
641 for ( i = 0; i < childcount; i++ )
642 {
643 GhtNode *nc = NULL;
644 GHT_TRY(ght_node_read(reader, &nc));
645 if ( nc )
646 {
647 GHT_TRY(ght_node_add_child(n, nc));
648 }
649 }
650
651 *node = n;
652 return GHT_OK;
653 }
654
655 /* Recursively build a nodelist from a tree of GhtNodes */
656 GhtErr
ght_node_to_nodelist(const GhtNode * node,GhtNodeList * nodelist,GhtAttribute * attr,GhtHash * hash)657 ght_node_to_nodelist(const GhtNode *node, GhtNodeList *nodelist, GhtAttribute *attr, GhtHash *hash)
658 {
659 static int hash_array_len = GHT_MAX_HASH_LENGTH + 1;
660 GhtHash h[hash_array_len];
661 GhtAttribute *a;
662
663 /* Add our part of the hash to the incoming part */
664 memset(h, 0, hash_array_len);
665 strncpy(h, hash, hash_array_len);
666 if ( node->hash )
667 {
668 strcat(h, node->hash);
669 }
670
671 /* Make a copy of all the incoming attributes */
672 GHT_TRY(ght_attribute_union(node->attributes, attr, &a));
673
674 /* Recurse down to leaf nodes, copying attributes and passing them down */
675 if ( node->children && node->children->num_nodes > 0 )
676 {
677 int i;
678 for ( i = 0; i < node->children->num_nodes; i++ )
679 {
680 GHT_TRY(ght_node_to_nodelist(node->children->nodes[i], nodelist, a, h));
681 }
682 ght_attribute_free(a);
683 }
684 /* This is a leaf node, create a new node and add to list */
685 else
686 {
687 GhtNode *n;
688 GHT_TRY(ght_node_new_from_hash(h, &n));
689 GHT_TRY(ght_node_add_attribute(n, a));
690 GHT_TRY(ght_nodelist_add_node(nodelist, n));
691 }
692
693
694 return GHT_OK;
695 }
696
697 /* Recursively build a nodelist from a tree of GhtNodes */
698 GhtErr
ght_node_get_extent(const GhtNode * node,const GhtHash * hash,GhtArea * area)699 ght_node_get_extent(const GhtNode *node, const GhtHash *hash, GhtArea *area)
700 {
701 static int hash_array_len = GHT_MAX_HASH_LENGTH + 1;
702 GhtHash h[hash_array_len];
703 GhtCoordinate coord;
704
705 /* Add our part of the hash to the incoming part */
706 memset(h, 0, hash_array_len);
707 strncpy(h, hash, hash_array_len);
708 if ( node->hash )
709 strcat(h, node->hash);
710
711 if ( node->children && node->children->num_nodes > 0 )
712 {
713 int i;
714 for ( i = 0; i < node->children->num_nodes; i++ )
715 {
716 if ( node->children->nodes[i] && node->children->nodes[i]->hash )
717 {
718 ght_node_get_extent(node->children->nodes[i], h, area);
719 }
720 }
721 }
722 else
723 {
724 ght_coordinate_from_hash(h, &coord);
725 if ( coord.x < area->x.min ) area->x.min = coord.x;
726 if ( coord.x > area->x.max ) area->x.max = coord.x;
727 if ( coord.y < area->y.min ) area->y.min = coord.y;
728 if ( coord.y > area->y.max ) area->y.max = coord.y;
729 }
730
731 return GHT_OK;
732 }
733
734 GhtErr
ght_node_filter_by_attribute(const GhtNode * node,const GhtFilter * filter,GhtNode ** filtered_node)735 ght_node_filter_by_attribute(const GhtNode *node, const GhtFilter *filter, GhtNode **filtered_node)
736 {
737 int i;
738 double val;
739 int keep = 1;
740 GhtAttribute *attr;
741 GhtNode *node_copy = NULL;
742
743 /* Our default position is nothing is getting returned */
744 *filtered_node = NULL;
745
746 /* No-op on an empty input */
747 if ( ! node )
748 return GHT_OK;
749
750 attr = node->attributes;
751 while ( attr )
752 {
753 /* Only filter on the attribute of interest */
754 if ( attr->dim != filter->dim )
755 {
756 attr = attr->next;
757 }
758 else
759 {
760 GHT_TRY(ght_attribute_get_value(attr, &val));
761 switch ( filter->mode )
762 {
763 case GHT_GREATER_THAN:
764 keep = (val > filter->range.min);
765 break;
766 case GHT_LESS_THAN:
767 keep = (val < filter->range.max);
768 break;
769 case GHT_BETWEEN:
770 keep = ((val <= filter->range.max) && (val >= filter->range.min));
771 break;
772 case GHT_EQUAL:
773 keep = (val == filter->range.min);
774 break;
775 default:
776 ght_error("%s: invalid GhtFilterMode (%d)", __func__, filter->mode);
777 }
778 break;
779 }
780 }
781
782 /* We found a relevant attribute, and it failed the filter test. */
783 /* So, this node (and all it's children) can be excluded! */
784 if ( ! keep )
785 {
786 return GHT_OK;
787 }
788
789 /* Also take copies of any children that pass the filter */
790 if ( node->children )
791 {
792 for ( i = 0; i < node->children->num_nodes; i++ )
793 {
794 GhtNode *child_copy;
795 GHT_TRY(ght_node_filter_by_attribute(node->children->nodes[i], filter, &child_copy));
796 /* Child survived the filtering */
797 if ( child_copy )
798 {
799 if ( ! node_copy )
800 {
801 GHT_TRY(ght_node_new(&node_copy));
802 GHT_TRY(ght_hash_clone(node->hash, &(node_copy->hash)));
803 GHT_TRY(ght_attribute_clone(node->attributes, &(node_copy->attributes)));
804 }
805 GHT_TRY(ght_node_add_child(node_copy, child_copy));
806 }
807 }
808 }
809 else
810 {
811 GHT_TRY(ght_node_new(&node_copy));
812 GHT_TRY(ght_hash_clone(node->hash, &(node_copy->hash)));
813 GHT_TRY(ght_attribute_clone(node->attributes, &(node_copy->attributes)));
814 }
815
816 /* Done, return the structure */
817 *filtered_node = node_copy;
818 return GHT_OK;
819 }