2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * Copyright (C) 2008 Liam Girdwood
19 #define _GNU_SOURCE /* for strtof and NAN */
28 #include <sys/types.h>
33 #include <libastrodb/db.h>
34 #include <libastrodb/table.h>
35 #include <libastrodb/wget.h>
36 #include <libastrodb/gunzip.h>
37 #include <libastrodb/adbstdio.h>
40 #define FLOAT_SIZE '6' /* CDS F string size and below for float else double */
43 /*! \fn static int init_table(astrodb_table * table, int ra_stride, int dec_stride, int mag_stride)
44 * \brief Initialise a dataset
46 static int init_table(struct astrodb_table
*table
,
47 int ra_stride
, int dec_stride
, int mag_stride
)
49 struct astrodb_slist
**slist
, **hash_object
;
50 struct astrodb_tile_status
*status
;
53 table
->ra
.stride
= ra_stride
;
54 table
->dec
.stride
= dec_stride
;
55 table
->mag
.stride
= mag_stride
;
57 table
->array_size
= table
->ra
.stride
* table
->dec
.stride
*
58 table
->mag
.stride
* sizeof(struct astrodb_slist
*);
59 table
->status_size
= table
->ra
.stride
* table
->dec
.stride
*
60 table
->mag
.stride
* sizeof(struct astrodb_tile_status
);
61 table
->no_tiles
= table
->ra
.stride
* table
->dec
.stride
*
64 table
->ra
.stride_size
=
65 fabs((table
->db
->ra_max
- table
->db
->ra_min
) / table
->ra
.stride
);
66 table
->dec
.stride_size
=
67 fabs((table
->db
->dec_max
- table
->db
->dec_min
) / table
->dec
.stride
);
68 table
->mag
.stride_size
=
69 fabs((table
->db
->mag_bright
- table
->db
->mag_faint
) / table
->mag
.stride
);
71 slist
= (struct astrodb_slist
**) calloc(1, table
->array_size
);
76 table
->objects
= slist
;
78 status
= (struct astrodb_tile_status
*) calloc(1, table
->status_size
);
84 table
->status
= status
;
86 for (i
= 0; i
< table
->num_hmaps
; i
++) {
87 hash_object
= (struct astrodb_slist
**) calloc(1, table
->array_size
);
88 if (hash_object
== NULL
) {
92 free(table
->hmaps
[i
-1].hobjects
);
97 table
->hmaps
[i
].hobjects
= hash_object
;
103 /*! \fn static astrodb_table* create_table(astrodb_db*db, astrodb_dlist* desc, astrodb_dlist* files)
104 * \brief Create a object dataset and populate fields
106 static struct astrodb_table
*create_table(struct astrodb_db
*db
,
107 struct astrodb_dlist
*desc
, struct astrodb_dlist
*files
)
109 struct astrodb_table
*table
;
111 table
= calloc(1, sizeof(struct astrodb_table
));
115 bzero(table
, sizeof(struct astrodb_table
));
116 table
->byte_description
=
117 ((struct astrodb_table_info
*) files
->data
)->byte_description
;
118 table
->file
= ((struct astrodb_table_info
*) files
->data
)->name
;
119 table
->table_size
= ((struct astrodb_table_info
*) files
->data
)->records
;
120 table
->record_size
= ((struct astrodb_table_info
*) files
->data
)->length
;
128 /*! \fn astrodb_table *astrodb_table_create(astrodb_db *db, char *table_name, unsigned int flags)
130 * \param table_name Dataset name (dataset file name in ReadMe)
131 * \param flags Dataset creation flags.
132 * \return A astrodb_table* for success or NULL on error
134 * Creates a dataset object.
136 struct astrodb_table
*astrodb_table_create(struct astrodb_db
*db
,
137 char *table_name
, unsigned int flags
)
139 struct astrodb_dlist
*files
= db
->info
->files
;
140 struct astrodb_table
*table
= NULL
;
141 astrodb_info("Scanning for key fields in table: %s\n", table_name
);
144 struct astrodb_table_info
*table_info
=
145 (struct astrodb_table_info
*) files
->data
;
146 struct astrodb_dlist
*byte_desc
= table_info
->byte_description
;
148 /* create a tile array per file for files that have a byte desc */
150 !strncmp(table_name
, table_info
->name
, strlen(table_name
))) {
152 table
= create_table(db
, byte_desc
, files
);
153 table
->init_flags
= flags
;
154 astrodb_info(" scan: %s\n", table_info
->name
);
156 db
->tables
= astrodb_dlist_append(db
->tables
,
166 /*! \fn int astrodb_table_open(astrodb_table * table, astrodb_progress progress, int ra, int dec, int mag)
167 * \param table dataset
168 * \param progress Progress callback
169 * \param ra RA stride
170 * \param dec Dec stride
171 * \param mag Magnitude stride
173 * Initialise a dataset. This will download and import the raw data if necessary.
175 int astrodb_table_open(struct astrodb_table
*table
, int ra
, int dec
, int mag
)
177 struct stat stat_info
;
182 sprintf(pstr
, "Initialising table %s", table
->file
);
184 /* do we have to delete existing dataset */
185 //if (flags & ADB_REFRESH)
186 // delete_table(astrodb_table* table);
188 table_get_object_insert(table
);
190 ret
= init_table(table
, ra
, dec
, mag
);
192 astrodb_error("failed to initialise table %d\n", ret
);
196 /* check for local binary copy */
197 sprintf(file
, "%s%s%s", table
->db
->local_path
, table
->file
, ".idx");
198 ret
= stat(file
, &stat_info
);
200 ret
= table_load_hdr(table
);
202 astrodb_error("failed to load binary header %s %d\n",
206 ret
= table_uncache_tiles(table
);
208 astrodb_error("failed to uncache table tiles %d\n",
213 /* check for local ASCII copy */
214 sprintf(file
, "%s%s", table
->db
->local_path
, table
->file
);
215 ret
= stat(file
, &stat_info
);
219 /* check for local */
221 /* local binary or ASCII not available, so download ASCII */
222 if (!(strncmp("http://", table
->db
->lib
->remote
, 7)) ||
223 !(strncmp("ftp://", table
->db
->lib
->remote
, 6))) {
224 ret
= table_get_remote_dataset(table
);
226 astrodb_error("failed to get remote table %d\n", ret
);
231 if (table
->init_flags
& ADB_MEM
) {
232 ret
= table_import(table
, file
);
234 astrodb_error("failed to import table %s %d\n", file
,
239 if (table
->init_flags
& ADB_FILE
) {
240 ret
= table_save_hdr(table
);
242 astrodb_error("failed to save table schema %d\n", ret
);
245 ret
= table_cache_tiles(table
);
250 /*! \fn void astrodb_table_close(astrodb_table* table)
251 * \param table dataset
253 * Free dataset resources
255 void astrodb_table_close(struct astrodb_table
*table
)
258 struct astrodb_db
*db
= table
->db
;
261 for (i
= 0; i
< table
->num_hmaps
; i
++) {
262 for (j
= 0; j
< table
->no_tiles
; j
++) {
264 struct astrodb_slist
*slist
, *slist_
;
265 slist_
= *(table
->hmaps
[i
].hobjects
+ j
);
269 slist_
= slist_
->tail
;
274 free(table
->hmaps
[i
].hobjects
);
277 if (table
->objects
) {
278 /* free objects in array */
279 for (i
= 0; i
< table
->no_tiles
; i
++)
280 astrodb_slist_foreach_free(*(table
->objects
+ i
), NULL
, 0);
282 free(table
->objects
);
288 db
->tables
= astrodb_dlist_free_object(db
->tables
, table
);
291 /*! \fn void astrodb_table_put_objects(astrodb_slist *results)
292 * \param results Results from astrodb_table_get_objects()
294 * Releases resources held by the results from astrodb_table_get_objects().
296 void astrodb_table_put_objects(struct astrodb_slist
*results
)
298 struct astrodb_slist
*slist
;
302 results
= results
->tail
;
307 /*! \fn int astrodb_table_alt_column(astrodb_table* table, char* field, char* alt, int flags)
308 * \param table dataset
309 * \param field Primary field
310 * \param alt Alternative field
313 * Set an alternative import field if the primary field is blank.
315 int astrodb_table_alt_column(struct astrodb_table
*table
, char *field
,
316 char *alt
, int flags
)
320 if (table
->alt_fields
>= TABLE_MAX_AIDX
) {
321 astrodb_error("too many alt fields %s\n", field
);
325 idx
= table_find_field(table
, field
);
327 astrodb_error("field not found %s\n", field
);
331 err
= table_add_alt_field(table
, alt
, idx
);
333 astrodb_error("failed to add field %s at idx %d\n", field
, idx
);
340 /*! \fn void astrodb_table_clip_on_position (astrodb_table* table, double min_ra, double min_dec, double max_ra, double max_dec, double faint_mag,double bright_mag)
341 * \param table dataset
342 * \param min_ra Min RA
343 * \param min_dec Min Dec
344 * \param max_ra Max RA
345 * \param max_dec Max Dec
346 * \param faint_mag Faint Magnitude
347 * \param bright_mag Bright Magnitude
349 * Clips the dataset to the within the given position range. Note: Clipping
350 * boundaries are not exact and depend on the granularity of the
351 * dataset position strides.
353 void astrodb_table_clip_on_position(struct astrodb_table
*table
,
354 double min_ra
, double min_dec
,
355 double max_ra
, double max_dec
,
356 double faint_mag
, double bright_mag
)
358 /* check ra,deg & mag boundaries */
359 if (min_ra
< table
->db
->ra_min
)
360 min_ra
= table
->db
->ra_min
;
361 if (max_ra
> table
->db
->ra_max
)
362 max_ra
= table
->db
->ra_max
;
363 if (min_dec
< table
->db
->dec_min
)
364 min_dec
= table
->db
->dec_min
;
365 if (max_dec
> table
->db
->dec_max
)
366 max_dec
= table
->db
->dec_max
;
367 if (faint_mag
> table
->db
->mag_faint
)
368 faint_mag
= table
->db
->mag_faint
;
369 if (bright_mag
< table
->db
->mag_bright
)
370 bright_mag
= table
->db
->mag_bright
;
372 /* normalise boundaries */
373 min_ra
-= table
->db
->ra_min
;
374 max_ra
-= table
->db
->ra_min
;
375 min_dec
-= table
->db
->dec_min
;
376 max_dec
-= table
->db
->dec_min
;
377 faint_mag
-= table
->db
->mag_bright
;
378 bright_mag
-= table
->db
->mag_bright
;
382 /* convert to stride boundaries */
383 table
->ra
.clip_min
= floor(min_ra
/ table
->ra
.stride_size
);
384 table
->ra
.clip_max
= floor(max_ra
/ table
->ra
.stride_size
);
385 table
->dec
.clip_min
= floor(min_dec
/ table
->dec
.stride_size
);
386 table
->dec
.clip_max
= floor(max_dec
/ table
->dec
.stride_size
);
388 table
->mag
.clip_max
= floor(bright_mag
/ table
->mag
.stride_size
);
389 table
->mag
.clip_min
= floor(faint_mag
/ table
->mag
.stride_size
);
392 /*! \fn void astrodb_table_clip_on_distance (astrodb_table* table, double min_AU, double max_AU)
393 * \param table dataset
394 * \param min_AU Min AU
395 * \param max_AU Max AU
397 * Clips the dataset to the within the given distance range. Note: Clipping
398 * boundaries are not exact and depend on the granularity of the
399 * dataset distance stride.
401 void astrodb_table_clip_on_distance(struct astrodb_table
*table
,
402 double min_AU
, double max_AU
)
404 /* check AU boundaries */
405 if (min_AU
< table
->db
->ra_min
)
406 min_AU
= table
->db
->ra_min
;
407 if (max_AU
> table
->db
->ra_max
)
408 max_AU
= table
->db
->ra_max
;
410 /* normalise boundaries */
411 min_AU
-= table
->db
->ra_min
;
412 max_AU
-= table
->db
->ra_min
;
416 /* convert to stride boundaries */
417 table
->ra
.clip_min
= (int) min_AU
/ table
->ra
.stride_size
;
418 table
->ra
.clip_max
= ((int) max_AU
/ table
->ra
.stride_size
) + 1;
421 /*! \fn void astrodb_table_clip_on_fov (astrodb_table* table, double ra, double dec, double radius,
422 double faint_mag, double bright_mag);
423 * \brief Set dataset clipping area based on field of view
426 void astrodb_table_clip_on_fov (struct astrodb_table
*table
,
427 double ra
, double dec
, double fov
,
428 double faint_mag
, double bright_mag
)
430 /* check boundaries */
432 if (ra
< table
->db
->ra_min
)
433 ra
= table
->db
->ra_min
;
434 if (ra
> table
->db
->ra_max
)
435 ra
= table
->db
->ra_max
;
436 if (dec
< table
->db
->dec_min
)
437 dec
= table
->db
->dec_min
;
438 if (dec
> table
->db
->dec_max
)
439 dec
= table
->db
->dec_max
;
440 if (faint_mag
> table
->db
->mag_faint
)
441 faint_mag
= table
->db
->mag_faint
;
442 if (bright_mag
< table
->db
->mag_bright
)
443 bright_mag
= table
->db
->mag_bright
;
445 table
->clip_centre_ra
= ra
;
446 table
->clip_centre_dec
= dec
;
448 /* normalise boundaries */
449 faint_mag
-= table
->db
->mag_bright
;
450 bright_mag
-= table
->db
->mag_bright
;
452 /* convert to stride boundaries */
453 table
->mag
.clip_max
= (int) bright_mag
/ table
->mag
.stride_size
;
454 table
->mag
.clip_min
= ((int) faint_mag
/ table
->mag
.stride_size
) + 1;
457 /*! \fn void astrodb_table_unclip (astrodb_table* table)
458 * \param table dataset
460 * Unclip the dataset to it's full boundaries
462 void astrodb_table_unclip(struct astrodb_table
*table
)
467 table
->ra
.clip_min
= 0;
468 table
->dec
.clip_min
= 0;
469 table
->ra
.clip_max
= table
->ra
.stride
- 1;
470 table
->dec
.clip_max
= table
->dec
.stride
- 1;
471 table
->mag
.clip_min
= table
->mag
.stride
- 1;
472 table
->mag
.clip_max
= 0;
475 for (i
= 0; i
<= table
->ra
.clip_max
; i
++) {
476 for (j
= 0; j
<= table
->dec
.clip_max
; j
++) {
477 for (k
= 0; k
<= table
->mag
.clip_min
; k
++) {
478 offset
= table_calc_offset(table
, i
, j
, k
);
479 (table
->status
+ offset
)->flags
&= ~ADB_SUSED
;
485 /*! int astrodb_table_get_objects (astrodb_table* table, astrodb_progress progress, astrodb_slist **result, unsigned int src)
486 * \param table dataset
487 * \param progress Progress callback
488 * \param results dataset objects
489 * \param src Get objects flags
490 * \return number of objects, or negative for failed
492 * Get objects from dataset based on clipping area. Results must be released
493 * with astrodb_table_put_objects() after use.
495 int astrodb_table_get_objects(struct astrodb_table
*table
,
496 struct astrodb_slist
**result
,
502 return table_get_objects_memc(table
, result
);
504 return table_get_objects_cachec(table
, result
);
506 return table_get_objects_rawc(table
, result
);
508 return table_get_objects_onlinec(table
, result
);
515 return table_get_objects_mem(table
, result
);
517 return table_get_objects_cache(table
, result
);
519 return table_get_objects_raw(table
, result
);
521 return table_get_objects_online(table
, result
);
528 /*! \fn int astrodb_table_prune(astrodb_table* table)
529 * \param table dataset
530 * \return bytes free'd
532 * Free's oldest unused object tiles.
534 int astrodb_table_prune(struct astrodb_table
*table
)
539 /*! \fn int astrodb_table_get_size(astrodb_table* table)
540 * \param table dataset
541 * \return size in bytes
543 * Get dataset memory usage.
545 int astrodb_table_get_size(struct astrodb_table
*table
)
547 return table
->object_size
* table
->db
->db_size
;
550 /*! \fn void astrodb_table_set_status_posn (astrodb_table* table, double min_ra, double min_dec, double max_ra, double max_dec, double faint_mag,double bright_mag, unsigned int status)
551 * \param table dataset
552 * \param min_ra Min RA
553 * \param min_dec Min Dec
554 * \param max_ra Max RA
555 * \param max_dec Max Dec
556 * \param faint_mag Faint Magnitude
557 * \param bright_mag Bright Magnitude
558 * \param status Object status
560 * Set the status of objects within a given position range.
562 void astrodb_table_set_status_posn(struct astrodb_table
*table
,
563 double min_ra
, double min_dec
,
564 double max_ra
, double max_dec
,
565 double faint_mag
, double bright_mag
,
569 int clip_min_ra
, clip_max_ra
, clip_min_dec
, clip_max_dec
,
570 clip_bright_mag
, clip_faint_mag
;
572 clip_min_ra
= table
->ra
.clip_min
;
573 clip_max_ra
= table
->ra
.clip_max
;
574 clip_min_dec
= table
->dec
.clip_min
;
575 clip_max_dec
= table
->dec
.clip_max
;
576 clip_bright_mag
= table
->mag
.clip_max
;
577 clip_faint_mag
= table
->mag
.clip_min
;
579 astrodb_table_clip_on_position(table
, min_ra
, min_dec
,
580 max_ra
, max_dec
, bright_mag
, faint_mag
);
582 for (i
= table
->ra
.clip_min
; i
< table
->ra
.clip_max
; i
++) {
583 for (j
= table
->dec
.clip_min
; j
< table
->dec
.clip_max
; j
++) {
584 for (k
= table
->mag
.clip_max
;
585 k
< table
->mag
.clip_min
; k
++) {
592 /*! \fn void astrodb_table_set_status_dist (astrodb_table* table, double min_AU, double max_AU, unsigned int status)
593 * \param table Dataset
594 * \param min_AU Minimum distance in AU
595 * \param max_AU Maximum distance in AU
596 * \param status Status flags to set
598 * Set the status of objects within a given distance range.
600 void astrodb_table_set_status_dist(struct astrodb_table
*table
,
601 double min_AU
, double max_AU
,
604 int min_offset
= (min_AU
- table
->db
->ra_min
) / table
->ra
.stride_size
;
605 int max_offset
= (max_AU
- table
->db
->ra_min
) / table
->ra
.stride_size
;
607 for (; min_offset
< max_offset
; min_offset
++) {
608 ((struct astrodb_tile_status
*)
609 (table
->status
+ min_offset
))->flags
= status
;
613 /*! \fn astrodb_tile_status astrodb_table_get_status_posn (astrodb_table* table, double ra, double dec, double mag)
614 * \param table Dataset
615 * \param ra RA in degrees
616 * \param dec DEC in dergrees
617 * \param mag Magnitude
619 * Get the status of objects within a tile at a give (ra,dec,mag) offset.
621 struct astrodb_tile_status
622 astrodb_table_get_status_posn(struct astrodb_table
*table
,
623 double ra
, double dec
, double mag
)
627 x
= floor((ra
- table
->db
->ra_min
) / table
->ra
.stride_size
);
628 y
= floor((dec
- table
->db
->dec_min
) / table
->dec
.stride_size
);
629 z
= floor((mag
- table
->db
->mag_bright
) / table
->mag
.stride_size
);
630 offset
= table_calc_offset(table
, x
, y
, z
);
632 return *(table
->status
+ offset
);
635 /*! \fn astrodb_tile_status astrodb_table_get_status_dist (astrodb_table* table, double AU)
636 * \param table Dataset
637 * \param AU Distance in AU
640 * Get the status of objects within a tile at a give distance offset.
642 struct astrodb_tile_status
643 astrodb_table_get_status_dist(struct astrodb_table
*table
, double AU
)
645 int offset
= floor((AU
- table
->db
->ra_min
) / table
->ra
.stride_size
);
646 return *(table
->status
+ offset
);
649 /*! \fn astrodb_ctype astrodb_table_get_column_type(astrodb_table* table, char* field)
650 * \param table Dataset
651 * \param field Dataset field name
654 * Get the C type for a field within a dataset.
656 astrodb_ctype
astrodb_table_get_column_type(struct astrodb_table
*table
,
661 for (i
= 0; i
< table
->object_fields
; i
++) {
662 if (!strcmp(table
->idx
[i
].symbol
, field
))
663 return table
->idx
[i
].type
;
669 /*! \fn int astrodb_table_get_column_offset(astrodb_table* table, char* field)
670 * \param table Dataset
671 * \param field Dataset field name
672 * \return Field offset in bytes
674 * Gets the offset in bytes for a field within a dataset.
676 int astrodb_table_get_column_offset(struct astrodb_table
*table
, char *field
)
680 /* check custom fields */
681 for (i
= 0; i
< table
->object_fields
; i
++) {
682 if (!strcmp(table
->idx
[i
].symbol
, field
)) {
683 return table
->idx
[i
].s_offset
;
687 astrodb_error("failed to find field %s\n", field
);
691 /*! \fn int astrodb_table_for_search_results_do(astrodb_slist* res, void(*func)(void* object, void* data), void* data)
692 * \param res Results from get_objects
693 * \param func Function to call per object in res
694 * \param data Pointer to pass to func
695 * \return 0 for success, negative for error
697 * Calls func for every object in res.
700 int astrodb_table_for_search_results_do(struct astrodb_slist
*results
,
701 void (*func
) (void *object
, void *data
),
708 struct astrodb_slist
*slist
= results
->data
;
710 func(slist
->data
, data
);
713 results
= results
->tail
;
720 /*! \fn int astrodb_table_get_row_size(astrodb_table* table);
722 int astrodb_table_get_row_size(struct astrodb_table
*table
)
724 return table
->object_size
;
728 /*! \fn void* astrodb_table_get_object (astrodb_table* table, char* id, char* field);
729 * \param table dataset
730 * \param id object id
731 * \param field dataset field
732 * \return object or NULL if not found
734 * Get an object based on it' ID
736 void* astrodb_table_get_object (struct astrodb_table
*table
, char *id
,
739 struct astrodb_slist
*slist
= NULL
;
742 for (i
= 0; i
< table
->num_hmaps
; i
++) {
743 if (!strncmp(field
, table
->hmaps
[i
].field
,
744 strlen(table
->hmaps
[i
].field
))) {
748 astrodb_error("failed to find field %s\n", field
);
752 offset
= table_calc_hash_str(id
, strlen(id
), table
->no_tiles
);
753 slist
= *(table
->hmaps
[i
].hobjects
+ offset
);
756 char* object_id
= slist
->data
+ table
->hmaps
[i
].offset
;
757 if (!strstr(object_id
, id
))
762 astrodb_error("failed to find id %s\n", id
);
766 /*! \fn int astrodb_table_hash_key(astrodb_table* table, char* field)
767 * \param table dataset
768 * \param field Field to be hashed.
769 * \return 0 on success
771 * Add a field to be hashed for fast lookups.
773 int astrodb_table_hash_key(struct astrodb_table
* table
, char* key
)
775 if (table
->num_hmaps
== HASH_MAPS
) {
776 astrodb_error("too many hashed keys %s\n", key
);
780 table
->hmaps
[table
->num_hmaps
].offset
=
781 astrodb_table_get_column_offset(table
, key
);
782 if (table
->hmaps
[table
->num_hmaps
].offset
< 0) {
783 astrodb_error("invalid column offset %s\n", key
);
787 strncpy(table
->hmaps
[table
->num_hmaps
].field
, key
, 8);
792 /*! \fn int astrodb_table_register_schema(astrodb_table* table, astrodb_schema_object* idx, int idx_size);
793 * \param table dataset
794 * \param idx Object field index
795 * \param idx_size Number of fields in index
796 * \return 0 on success
798 * Register a new custom object type
800 int astrodb_table_register_schema(struct astrodb_table
*table
,
801 struct astrodb_schema_object
*idx
,
802 int idx_size
, int object_size
)
806 for (i
= 0, n
= 0; i
< idx_size
; i
++) {
807 if (!table_add_custom_struct_field(table
, &idx
[i
]))
810 table
->object_size
= object_size
;