├── MANIFEST.in ├── .gitignore ├── pymangle ├── __init__.py ├── version.py ├── defs.h ├── pixel.h ├── point.h ├── rand.h ├── point.c ├── stack.h ├── polygon.h ├── rand.c ├── pixel.c ├── mangle.h ├── cap.h ├── stack.c ├── cap.c ├── mangle.c ├── mangle.py ├── polygon.c └── _mangle.c ├── tox.ini ├── register.py ├── .github └── workflows │ └── test.yml ├── setup.py ├── README.md └── tests └── test_pymangle.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include pymangle *.c *.h 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tox 2 | MANIFEST 3 | .cache 4 | pymangle.egg-info 5 | pymangle/*pyc 6 | __pycache__ 7 | -------------------------------------------------------------------------------- /pymangle/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from . import mangle 4 | from .mangle import Mangle, genrand_cap 5 | from .version import __version__ 6 | 7 | __doc__ = mangle.__doc__ 8 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{27,34,35,36}-{np,nonp} 3 | skip_missing_interpreters = true 4 | 5 | [testenv] 6 | 7 | deps = 8 | pytest 9 | np: numpy 10 | 11 | commands = 12 | py.test {posargs} 13 | -------------------------------------------------------------------------------- /pymangle/version.py: -------------------------------------------------------------------------------- 1 | # Store the version here so: 2 | # 1) we don't load dependencies by storing it in __init__.py 3 | # 2) we can import it in setup.py for the same reason 4 | # 3) we can import it into the module 5 | __version__ = '0.9.3' 6 | -------------------------------------------------------------------------------- /pymangle/defs.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_DEFINITIONS_H 2 | #define _MANGLE_DEFINITIONS_H 3 | 4 | #define _MANGLE_SMALL_BUFFSIZE 25 5 | #define _MANGLE_LARGE_BUFFSIZE 255 6 | 7 | #define _MANGLE_MAX_FILELEN 2048 8 | 9 | #define D2R 0.017453292519943295769 10 | #define R2D 57.2957795130823208767 11 | 12 | #include 13 | typedef int64_t int64; 14 | 15 | #define wlog(...) fprintf(stderr, __VA_ARGS__) 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /register.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | rname = 'README.md' 4 | rstname = 'README.rst' 5 | 6 | 7 | def clear(): 8 | if os.path.exists(rstname): 9 | os.remove(rstname) 10 | 11 | 12 | def try_make_rst(): 13 | cmd = "pandoc --from=markdown --to=rst {readme} -o {rst}" 14 | cmd = cmd.format(readme=rname, rst=rstname) 15 | os.system(cmd) 16 | 17 | 18 | def register(): 19 | os.system("python setup.py register") 20 | 21 | 22 | clear() 23 | 24 | try_make_rst() 25 | register() 26 | 27 | clear() 28 | -------------------------------------------------------------------------------- /pymangle/pixel.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_PIXLIST_H 2 | #define _MANGLE_PIXLIST_H 3 | 4 | #include "mangle.h" 5 | #include "point.h" 6 | #include "stack.h" 7 | 8 | struct PixelListVec { 9 | char pixeltype; 10 | int64 pixelres; 11 | 12 | // a vector of int64 stacks 13 | size_t size; 14 | struct i64stack** data; 15 | }; 16 | 17 | 18 | struct PixelListVec* 19 | PixelListVec_new(size_t n); 20 | struct PixelListVec* PixelListVec_free(struct PixelListVec* self); 21 | 22 | // extract the pixel scheme and resolution from the input string 23 | // which sould be [res][scheme] e.g. 9s 24 | int pixel_parse_scheme(char buff[_MANGLE_SMALL_BUFFSIZE], 25 | int64* res, char* pixeltype); 26 | 27 | 28 | int64 get_pixel_simple(int64 pixelres, struct Point* pt); 29 | 30 | 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /pymangle/point.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_POINT_H 2 | #define _MANGLE_POINT_H 3 | 4 | struct Point { 5 | long double theta; // theta in radians (=dec-pi/2) 6 | long double phi; // phi in radians (=ra) 7 | long double x; 8 | long double y; 9 | long double z; 10 | }; 11 | 12 | void point_set_from_radec(struct Point* pt, long double ra, long double dec); 13 | void point_set_from_thetaphi(struct Point* pt, long double theta, long double phi); 14 | void radec_from_point(struct Point* pt, long double *ra, long double *dec); 15 | 16 | /* 17 | ra = phi*R2D; 18 | dec = 90.0 - theta*R2D; 19 | */ 20 | void radec_from_thetaphi(long double theta, 21 | long double phi, 22 | long double *ra, 23 | long double *dec); 24 | /* 25 | theta = (90.0-dec)*D2R; 26 | phi = ra*D2R; 27 | */ 28 | void thetaphi_from_radec(long double ra, 29 | long double dec, 30 | long double *theta, 31 | long double *phi); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /pymangle/rand.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_RAND_H 2 | #define _MANGLE_RAND_H 3 | 4 | #include "point.h" 5 | void seed_random(void); 6 | 7 | void genrand_allsky(struct Point *pt); 8 | void genrand_range(long double cthmin, long double cthmax, 9 | long double phimin, long double phimax, 10 | struct Point *pt); 11 | 12 | void genrand_theta_phi_allsky(long double* theta, long double* phi); 13 | 14 | void genrand_theta_phi(long double cthmin, long double cthmax, 15 | long double phimin, long double phimax, 16 | long double* theta, long double* phi); 17 | 18 | /* 19 | * convert an ra/dec range to cos(theta) and phi range 20 | * for quicker use by the random range generators 21 | */ 22 | 23 | int 24 | radec_range_to_costhetaphi(long double ramin, long double ramax, 25 | long double decmin, long double decmax, 26 | long double* cthmin, long double* cthmax, 27 | long double* phimin, long double* phimax); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: pymangle CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | - releases/* 11 | 12 | jobs: 13 | tests: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ ubuntu-latest ] 18 | py: [ "3.11" ] 19 | CC: [ gcc ] 20 | CXX: [ g++ ] 21 | 22 | defaults: 23 | run: 24 | # cf. https://github.com/conda-incubator/setup-miniconda#important 25 | shell: bash -l {0} 26 | 27 | steps: 28 | - uses: actions/checkout@v2 29 | - uses: conda-incubator/setup-miniconda@v2 30 | with: 31 | python-version: ${{ matrix.pyver }} 32 | channels: conda-forge,defaults 33 | channel-priority: strict 34 | show-channel-urls: true 35 | miniforge-version: latest 36 | 37 | - name: Install deps 38 | run: | 39 | conda config --set always_yes yes 40 | conda install numpy pytest flake8 41 | 42 | - name: Install pymangle 43 | run: 44 | pip install . 45 | 46 | - name: lint 47 | run: | 48 | flake8 pymangle 49 | 50 | - name: Run tests 51 | run: | 52 | pytest -vv tests/ 53 | -------------------------------------------------------------------------------- /pymangle/point.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mangle.h" 5 | #include "point.h" 6 | 7 | void 8 | point_set_from_radec(struct Point* pt, long double ra, long double dec) { 9 | 10 | long double stheta=0; 11 | 12 | if (pt != NULL) { 13 | pt->phi = ra*D2R; 14 | pt->theta = (90.0-dec)*D2R; 15 | 16 | stheta = sinl(pt->theta); 17 | pt->x = stheta*cosl(pt->phi); 18 | pt->y = stheta*sinl(pt->phi); 19 | pt->z = cosl(pt->theta); 20 | } 21 | } 22 | void 23 | point_set_from_thetaphi(struct Point* pt, long double theta, long double phi) { 24 | 25 | long double stheta=0; 26 | 27 | if (pt != NULL) { 28 | pt->phi = phi; 29 | pt->theta = theta; 30 | 31 | stheta = sinl(pt->theta); 32 | pt->x = stheta*cosl(pt->phi); 33 | pt->y = stheta*sinl(pt->phi); 34 | pt->z = cosl(pt->theta); 35 | } 36 | } 37 | void 38 | radec_from_point(struct Point* pt, long double *ra, long double *dec) { 39 | *ra = pt->phi*R2D; 40 | *dec = 90.0 - pt->theta*R2D; 41 | } 42 | 43 | void radec_from_thetaphi(long double theta, 44 | long double phi, 45 | long double *ra, 46 | long double *dec) 47 | { 48 | *ra = phi*R2D; 49 | *dec = 90.0 - theta*R2D; 50 | } 51 | void thetaphi_from_radec(long double ra, 52 | long double dec, 53 | long double *theta, 54 | long double *phi) 55 | { 56 | *theta = (90.0-dec)*D2R; 57 | *phi = ra*D2R; 58 | } 59 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import glob 2 | 3 | from setuptools import setup 4 | from setuptools.extension import Extension 5 | from setuptools.command import build_ext 6 | 7 | 8 | ext = Extension("pymangle._mangle", ["pymangle/_mangle.c", 9 | "pymangle/mangle.c", 10 | "pymangle/cap.c", 11 | "pymangle/polygon.c", 12 | "pymangle/pixel.c", 13 | "pymangle/point.c", 14 | "pymangle/stack.c", 15 | "pymangle/rand.c"]) 16 | 17 | 18 | class BuildExt(build_ext.build_ext): 19 | '''Custom build_ext command to hide the numpy import 20 | Inspired by http://stackoverflow.com/a/21621689/1860757''' 21 | def finalize_options(self): 22 | '''add numpy includes to the include dirs''' 23 | build_ext.build_ext.finalize_options(self) 24 | import numpy as np 25 | self.include_dirs.append(np.get_include()) 26 | self.include_dirs.extend(glob.glob("pymangle/*h")) 27 | 28 | 29 | __version__ = None 30 | exec(open('pymangle/version.py').read()) 31 | 32 | description = "A python code to read and work with Mangle masks." 33 | 34 | with open('README.md') as fobj: 35 | long_description = fobj.read() 36 | 37 | setup(name="pymangle", 38 | packages=['pymangle'], 39 | description=description, 40 | long_description=long_description, 41 | long_description_content_type='text/markdown', 42 | version=__version__, 43 | license="GPL", 44 | install_requires=['numpy'], 45 | ext_modules=[ext], 46 | setup_requires=['numpy'], 47 | cmdclass={'build_ext': BuildExt}, 48 | include_package_data=True, 49 | ) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pymangle 2 | ======== 3 | 4 | A python code to read and work with Mangle masks. 5 | 6 | The underlying code is in C for speed, and is based on some C++ code written by 7 | Martin White. Martin has posted his C++ code at 8 | https://github.com/martinjameswhite/litemangle 9 | 10 | Currently supports ascii mangle polygon files, with and without pixelization. 11 | The code works in python2 and python3 12 | 13 | Authors: Erin Sheldon. Long double support added by Eli Rykoff. 14 | 15 | examples 16 | -------- 17 | 18 | ```python 19 | import pymangle 20 | 21 | # read a mangle polygon file 22 | m=pymangle.Mangle("mask.ply") 23 | 24 | # test an ra,dec point against the mask 25 | good = m.contains(200.0, 0.0) 26 | 27 | # test arrays of ra,dec points against the mask 28 | ra=numpy.array([200.0, 152.7]) 29 | dec=numpy.array([0.0, -15.0]) 30 | good = m.contains(ra, dec) 31 | 32 | # get the polygon ids 33 | ids = m.polyid(ra,dec) 34 | 35 | # get the weights 36 | weights = m.weight(ra,dec) 37 | 38 | # get poth polyids and weights 39 | ids, weights = m.polyid_and_weight(ra, dec) 40 | 41 | # generate random points 42 | ra_rand, dec_rand = m.genrand(1000) 43 | 44 | # generate randoms from the mask and with the additional constraint that they 45 | # are within the specified rectangle. This speeds things up if your mask is 46 | # relatively small compared to the full sphere; choose the box just big enough 47 | # to contain the mask. 48 | 49 | ra_min=200.0 50 | ra_max=210.0 51 | dec_min=0.0 52 | dec_max=10.0 53 | ra_rand, dec_rand = m.genrand_range(1000,ra_min,ra_max,dec_min,dec_max) 54 | 55 | # get the polygon weights 56 | weights = m.weights 57 | 58 | # read in a new set of weights 59 | m.read_weightfile(weightfile) 60 | 61 | # set the weights 62 | 63 | m.weights = weight_array 64 | ``` 65 | 66 | build and install python library 67 | -------------------------------- 68 | 69 | ```bash 70 | # using pip 71 | pip install pymangle 72 | 73 | # from source 74 | python setup.py install --prefix=/some/path 75 | ``` 76 | 77 | tests 78 | ----- 79 | ```bash 80 | python -c 'import pymangle; pymangle.test.test() 81 | ``` 82 | -------------------------------------------------------------------------------- /pymangle/stack.h: -------------------------------------------------------------------------------- 1 | // This header was auto-generated 2 | #ifndef _STACK_H 3 | #define _STACK_H 4 | #include 5 | 6 | #define STACK_PUSH_REALLOC_MULT 1 7 | #define STACK_PUSH_REALLOC_MULTVAL 2 8 | #define STACK_PUSH_INITSIZE 1 9 | 10 | struct i64stack { 11 | size_t size; // number of elements that are visible to the user 12 | size_t allocated_size; // number of allocated elements in data vector 13 | size_t push_realloc_style; // Currently always STACK_PUSH_REALLOC_MULT, 14 | // which is reallocate to allocated_size*realloc_multval 15 | size_t push_initsize; // default size on first push, default STACK_PUSH_INITSIZE 16 | double realloc_multval; // when allocated size is exceeded while pushing, 17 | // reallocate to allocated_size*realloc_multval, default 18 | // STACK_PUSH_REALLOC_MULTVAL 19 | // if allocated_size was zero, we allocate to push_initsize 20 | int64_t* data; 21 | }; 22 | 23 | struct i64stack* i64stack_new(size_t num); 24 | 25 | // if size > allocated size, then a reallocation occurs 26 | // if size <= internal size, then only the ->size field is reset 27 | // use i64stack_realloc() to reallocate the data vector and set the ->size 28 | void i64stack_resize(struct i64stack* stack, size_t newsize); 29 | 30 | // perform reallocation on the underlying data vector. This does 31 | // not change the size field unless the new size is smaller 32 | // than the viewed size 33 | void i64stack_realloc(struct i64stack* stack, size_t newsize); 34 | 35 | // completely clears memory in the data vector 36 | void i64stack_clear(struct i64stack* stack); 37 | 38 | // clears all memory and sets pointer to NULL 39 | // usage: stack=i64stack_delete(stack); 40 | struct i64stack* i64stack_delete(struct i64stack* stack); 41 | 42 | // if reallocation is needed, size is increased by 50 percent 43 | // unless size is zero, when it 100 are allocated 44 | void i64stack_push(struct i64stack* stack, int64_t val); 45 | // pop the last element and decrement size; no reallocation is performed 46 | // if empty, INT64_MIN is returned 47 | int64_t i64stack_pop(struct i64stack* stack); 48 | 49 | int __i64stack_compare_el(const void *a, const void *b); 50 | void i64stack_sort(struct i64stack* stack); 51 | int64_t* i64stack_find(struct i64stack* stack, int64_t el); 52 | 53 | #endif // header guard 54 | -------------------------------------------------------------------------------- /pymangle/polygon.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_POLYGON_H 2 | #define _MANGLE_POLYGON_H 3 | 4 | #include 5 | #include "defs.h" 6 | #include "point.h" 7 | #include "cap.h" 8 | 9 | struct Polygon { 10 | 11 | // these considered exposed 12 | int64 poly_id; 13 | int64 pixel_id; // optional 14 | long double weight; 15 | 16 | int area_set; 17 | long double area; // in str 18 | 19 | struct CapVec* caps; 20 | 21 | }; 22 | 23 | // new polygon with capvec that has the default capacity (CAPVEC_INITCAP) but size 0 24 | // the metadata is -9999 with ->area_set==0 25 | struct Polygon* polygon_new(void); 26 | 27 | // similar to new() but n caps zerod according to capvec_zeros(n) 28 | struct Polygon* polygon_zeros(size_t n); 29 | 30 | // completely free all data 31 | struct Polygon* polygon_free(struct Polygon* self); 32 | 33 | // full copy of all metadata and caps 34 | struct Polygon* polygon_copy(const struct Polygon* self); 35 | 36 | // reserve the specified number of caps 37 | int polygon_reserve(struct Polygon* self, size_t new_capacity); 38 | 39 | // resize the underlying cap vector 40 | int polygon_resize(struct Polygon* self, size_t new_size); 41 | 42 | // All metadata reset, caps cleared 43 | int polygon_clear(struct Polygon* self); 44 | 45 | // push a new cap onto the polygon (underlying caps vector) 46 | int polygon_push_cap(struct Polygon* self, const struct Cap* cap); 47 | 48 | // return a copy of the last Cap in the caps vector and decrement the size 49 | // member 50 | // 51 | // If the size is already 0, the return value is just the remaining element at 52 | // position 0, which might be garbage 53 | 54 | struct Cap polygon_pop_cap(struct Polygon* self); 55 | 56 | // loop through caps and see if any have zero area. 57 | // this does not use the ->area member 58 | // adapted from gzeroar, A J S Hamilton 59 | int polygon_has_zero_area(const struct Polygon* self); 60 | 61 | int read_into_polygon(FILE* fptr, struct Polygon* ply); 62 | int read_polygon_header(FILE* fptr, struct Polygon* ply, size_t* ncaps); 63 | 64 | int is_in_poly(struct Polygon* ply, struct Point* pt); 65 | 66 | int scan_expected_value(FILE* fptr, char* buff, const char* expected_value); 67 | 68 | 69 | void print_polygon(FILE* fptr, struct Polygon* self); 70 | 71 | struct PolyVec { 72 | size_t size; 73 | struct Polygon* data; 74 | }; 75 | 76 | struct PolyVec* polyvec_new(size_t n); 77 | struct PolyVec* polyvec_free(struct PolyVec* self); 78 | struct PolyVec *read_polygons(FILE* fptr, size_t npoly); 79 | void print_polygons(FILE* fptr, struct PolyVec *self); 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /pymangle/rand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "rand.h" 6 | #include "point.h" 7 | #include "defs.h" 8 | 9 | void 10 | seed_random(void) { 11 | struct timeval tm; 12 | gettimeofday(&tm, NULL); 13 | srand48((long) (tm.tv_sec * 1000000 + tm.tv_usec)); 14 | } 15 | 16 | 17 | void genrand_allsky(struct Point *pt) 18 | { 19 | long double theta, phi; 20 | genrand_theta_phi_allsky(&theta, &phi); 21 | point_set_from_thetaphi(pt, theta, phi); 22 | } 23 | void genrand_range(long double cthmin, long double cthmax, 24 | long double phimin, long double phimax, 25 | struct Point *pt) 26 | { 27 | long double theta, phi; 28 | genrand_theta_phi(cthmin, cthmax, 29 | phimin, phimax, 30 | &theta, &phi); 31 | 32 | point_set_from_thetaphi(pt, theta, phi); 33 | } 34 | int 35 | radec_range_to_costhetaphi(long double ramin, long double ramax, 36 | long double decmin, long double decmax, 37 | long double* cthmin, long double* cthmax, 38 | long double* phimin, long double* phimax) 39 | { 40 | 41 | if (ramin < 0.0 || ramax > 360.0) { 42 | wlog("ra range must be in [0,360] got [%.18Lg,%.18Lg]", 43 | ramin,ramax); 44 | return 0; 45 | } 46 | if (decmin < -90.0 || decmax > 90.0) { 47 | wlog("dec range must be in [-90,90] got [%.18Lg,%.18Lg]", 48 | decmin,decmax); 49 | return 0; 50 | } 51 | 52 | *cthmin = cosl((90.0-decmin)*D2R); 53 | *cthmax = cosl((90.0-decmax)*D2R); 54 | *phimin = ramin*D2R; 55 | *phimax = ramax*D2R; 56 | return 1; 57 | } 58 | 59 | 60 | 61 | /* 62 | * constant in cos(theta) 63 | */ 64 | void 65 | genrand_theta_phi_allsky(long double* theta, long double* phi) 66 | { 67 | *phi = (long double) drand48()*2*M_PI; 68 | // this is actually cos(theta) for now 69 | *theta = 2*(long double) drand48()-1; 70 | 71 | if (*theta > 1) *theta=1; 72 | if (*theta < -1) *theta=-1; 73 | 74 | *theta = acosl(*theta); 75 | } 76 | 77 | /* 78 | * Generate random points in a range. Inputs are 79 | * min(cos(theta)), max(cos(theta)), min(phi), max(phi) 80 | * 81 | * constant in cos(theta) 82 | */ 83 | void 84 | genrand_theta_phi(long double cthmin, long double cthmax, long double phimin, long double phimax, 85 | long double* theta, long double* phi) 86 | { 87 | 88 | // at first, theta is cos(theta) 89 | *phi = phimin + (phimax - phimin)*(long double) drand48(); 90 | 91 | // this is actually cos(theta) for now 92 | *theta = cthmin + (cthmax-cthmin)*(long double) drand48(); 93 | 94 | if (*theta > 1) *theta=1; 95 | if (*theta < -1) *theta=-1; 96 | 97 | *theta = acosl(*theta); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /pymangle/pixel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mangle.h" 7 | #include "pixel.h" 8 | #include "stack.h" 9 | #include "defs.h" 10 | 11 | struct PixelListVec* 12 | PixelListVec_new(size_t n) 13 | { 14 | struct PixelListVec* self=NULL; 15 | size_t i=0; 16 | 17 | if (n <= 0) { 18 | wlog("Vectors must be size > 0, got %ld", n); 19 | return NULL; 20 | } 21 | self=calloc(1, sizeof(struct PixelListVec)); 22 | if (self == NULL) { 23 | wlog("Could not allocate pixel list vector"); 24 | return NULL; 25 | } 26 | // array of pointers. The pointers will be NULL 27 | self->data = calloc(n, sizeof(struct i64stack*)); 28 | if (self->data == NULL) { 29 | free(self); 30 | wlog("Could not allocate %ld pixel list pointers", n); 31 | return NULL; 32 | } 33 | 34 | for (i=0; idata[i] = i64stack_new(0); 36 | } 37 | self->size=n; 38 | return self; 39 | } 40 | 41 | struct PixelListVec* 42 | PixelListVec_free(struct PixelListVec* self) 43 | { 44 | size_t i=0; 45 | struct i64stack* s=NULL; 46 | if (self != NULL) { 47 | if (self->data != NULL) { 48 | for (i=0; isize; i++) { 49 | s = self->data[i]; 50 | if (s != NULL) { 51 | s=i64stack_delete(s); 52 | } 53 | } 54 | free(self->data); 55 | } 56 | 57 | free(self); 58 | } 59 | 60 | return self; 61 | } 62 | 63 | 64 | 65 | 66 | int pixel_parse_scheme(char buff[_MANGLE_SMALL_BUFFSIZE], 67 | int64* res, char* pixeltype) { 68 | int status=1; 69 | char pixres_buff[_MANGLE_SMALL_BUFFSIZE] = {0}; 70 | char* ptr=NULL; 71 | ssize_t res_bytes=0; 72 | 73 | ptr = strchr(buff, 's'); 74 | if (ptr == NULL) { 75 | status=0; 76 | wlog("Only support pix scheme s, got: '%s'", buff); 77 | goto _get_pix_scheme_errout; 78 | } 79 | *pixeltype = 's'; 80 | 81 | // extract the numerical prefactor, which is the resolution 82 | res_bytes = (ptr-buff); 83 | if (res_bytes > 9) { 84 | status=0; 85 | wlog("pix scheme designation too big: '%s'", buff); 86 | goto _get_pix_scheme_errout; 87 | } 88 | strncpy(pixres_buff, buff, res_bytes); 89 | 90 | if (1 != sscanf(pixres_buff, "%ld", res)) { 91 | status=0; 92 | wlog("Could not extract resolution from pix scheme: '%s'", buff); 93 | goto _get_pix_scheme_errout; 94 | } 95 | 96 | // negative resolution is equivalent to 'u' or unpixelized 97 | if (*res < 0) { 98 | *pixeltype='u'; 99 | } 100 | 101 | _get_pix_scheme_errout: 102 | return status; 103 | } 104 | 105 | int64 106 | get_pixel_simple(int64 pixelres, struct Point* pt) 107 | { 108 | int64 pix=0; 109 | 110 | int64 i=0; 111 | int64 ps=0, p2=1; 112 | long double cth=0; 113 | int64 n=0, m=0; 114 | if (pixelres > 0) { 115 | for (i=0; itheta); 120 | n = (cth==1.0) ? 0: (int64) ( ceill( (1.0-cth)/2 * p2 )-1 ); 121 | m = (int64) ( floorl( (pt->phi/2./M_PI)*p2 ) ); 122 | pix = p2*n+m + ps; 123 | 124 | } 125 | return pix; 126 | } 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /pymangle/mangle.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_MASK_H 2 | #define _MANGLE_MASK_H 3 | 4 | #include "defs.h" 5 | #include "pixel.h" 6 | #include "polygon.h" 7 | 8 | 9 | struct MangleMask { 10 | int64 npoly; 11 | struct PolyVec* poly_vec; 12 | 13 | long double total_area; 14 | int64 pixelres; 15 | int64 maxpix; 16 | char pixeltype; 17 | struct PixelListVec* pixel_list_vec; 18 | 19 | int snapped; 20 | int balkanized; 21 | int real; 22 | 23 | int verbose; 24 | 25 | char* filename; 26 | char buff[_MANGLE_SMALL_BUFFSIZE]; 27 | 28 | char weightfile[_MANGLE_MAX_FILELEN]; 29 | //int has_weightfile; 30 | //char* weightfile; 31 | 32 | 33 | // for error messages 34 | char err[_MANGLE_LARGE_BUFFSIZE]; 35 | 36 | }; 37 | 38 | struct MangleMask* mangle_new(void); 39 | struct MangleMask* mangle_free(struct MangleMask* self); 40 | void mangle_clear(struct MangleMask* self); 41 | void mangle_set_verbosity(struct MangleMask* self, int verbosity); 42 | 43 | void mangle_print(FILE* fptr, struct MangleMask* self, int verbosity); 44 | 45 | int mangle_read(struct MangleMask* self, const char* filename); 46 | int mangle_read_header(struct MangleMask* self, FILE *fptr); 47 | 48 | int mangle_read_weights(struct MangleMask* self, const char* filename); 49 | int mangle_set_weights(struct MangleMask* self, long double *weights); 50 | 51 | // sum the areas for all polygons and store in ->total_area 52 | void mangle_calc_area_and_maxpix(struct MangleMask* self); 53 | 54 | int set_pixel_map(struct MangleMask* self); 55 | 56 | 57 | 58 | /* 59 | * check the point against the mask. If found, will return the 60 | * id and weight. These default to -1 and 0 61 | * 62 | * this version does not use pixelization 63 | */ 64 | int mangle_polyid_and_weight_nopix(struct MangleMask *self, 65 | struct Point *pt, 66 | int64 *poly_id, 67 | long double *weight); 68 | 69 | /* 70 | * check the point against a pixelized mask. If found, will return the id and 71 | * weight. These default to -1 and 0 72 | */ 73 | 74 | int mangle_polyid_and_weight_pix(struct MangleMask *self, 75 | struct Point *pt, 76 | int64 *poly_id, 77 | long double *weight); 78 | 79 | 80 | /* 81 | * this chooses the right function based on pixeltype 82 | */ 83 | 84 | int mangle_polyid_and_weight(struct MangleMask *self, 85 | struct Point *pt, 86 | int64 *poly_id, 87 | long double *weight); 88 | 89 | /* 90 | * inline version 91 | * 92 | * using some magic: leaving val at the end of this 93 | * block lets it become the value in an expression, 94 | */ 95 | #define MANGLE_POLYID_AND_WEIGHT(self, pt, poly_id, weight) ({ \ 96 | int ret=0; \ 97 | if ( (self)->pixeltype == 'u') { \ 98 | ret=mangle_polyid_and_weight_nopix(self,pt,poly_id,weight); \ 99 | } else { \ 100 | ret=mangle_polyid_and_weight_pix(self,pt,poly_id,weight); \ 101 | } \ 102 | ret; \ 103 | }) 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /pymangle/cap.h: -------------------------------------------------------------------------------- 1 | #ifndef _MANGLE_CAP_H 2 | #define _MANGLE_CAP_H 3 | 4 | #include 5 | #include "point.h" 6 | 7 | struct Cap { 8 | long double x; 9 | long double y; 10 | long double z; 11 | long double cm; 12 | }; 13 | 14 | #define CAPVEC_INITCAP 1 15 | #define CAPVEC_PUSH_REALLOC_MULTVAL 2 16 | 17 | // ->size and ->data are considered exposed 18 | struct CapVec { 19 | // the visible size of the vector, can be zero 20 | size_t size; 21 | 22 | // the capacity of the vector. For valid vectors this 23 | // will always be >= 1 24 | size_t capacity; 25 | 26 | // the underlying data array 27 | struct Cap* data; 28 | }; 29 | 30 | // specialized cap for generating random points 31 | // this saves some information needed for each random 32 | struct CapForRand { 33 | long double theta; // theta in radians (=dec-pi/2) 34 | long double phi; // phi in radians (=ra) 35 | long double cos_theta; 36 | long double sin_theta; 37 | long double cos_phi; 38 | long double sin_phi; 39 | 40 | long double angle; // angular "radius" of cap in radians 41 | // can calculate from a Cap with acosl(1-cm) 42 | }; 43 | 44 | // new capvec with the default capacity (CAPVEC_INITCAP) but size 0 45 | struct CapVec* capvec_new(void); 46 | 47 | // new capvec with specified number of elements. All data 48 | // are zerod 49 | struct CapVec* capvec_zeros(size_t n); 50 | 51 | // make sure the backing data array has at least the requested 52 | // number of elements. The ->size member is unchanged 53 | int capvec_reserve(struct CapVec* self, size_t new_capacity); 54 | 55 | // resize the vector; if the new size is larger than the old 56 | // *capacity*, then a reallocation is performed, otherwise 57 | // the underlying data array is not changed, only the size 58 | // member is changed 59 | int capvec_resize(struct CapVec* self, size_t new_size); 60 | 61 | // set the size to zero and the reallocate to the default capacity 62 | // (CAPVEC_INITCAP) this means the data should be considered lost 63 | int capvec_clear(struct CapVec* self); 64 | 65 | // push a new Cap onto the end of the vector, and increment the size 66 | // member 67 | // 68 | // the data of the cap is copied 69 | // 70 | // the capacity can be changed if the new size exceeds the capacity 71 | // resulting in a reallocation 72 | int capvec_push(struct CapVec* self, const struct Cap* cap); 73 | 74 | // return a copy of the last Cap in the vector and decrement the size 75 | // member 76 | // 77 | // If the size is already 0, the return value is just the remaining 78 | // element at position 0, which might be garbage 79 | struct Cap capvec_pop(struct CapVec* self); 80 | 81 | // completely free all memory and return NULL 82 | struct CapVec* capvec_free(struct CapVec* self); 83 | 84 | // get a full copy of the vector in new CapVec 85 | struct CapVec* capvec_copy(const struct CapVec* self); 86 | 87 | /* 88 | Find the smallest cap in the cap vector 89 | 90 | Adapted from cmminf A J S Hamilton 2001 91 | */ 92 | 93 | void capvec_min_cm(const struct CapVec* self, 94 | size_t *index, 95 | long double* cm_min); 96 | 97 | void cap_set(struct Cap* self, 98 | long double x, 99 | long double y, 100 | long double z, 101 | long double cm); 102 | 103 | int read_cap(FILE* fptr, struct Cap* self); 104 | void print_cap(FILE* fptr, struct Cap* self); 105 | void snprint_cap(const struct Cap* self, char *buff, size_t n); 106 | 107 | int is_in_cap(struct Cap* cap, struct Point* pt); 108 | 109 | /* 110 | generating random points in a cap. 111 | 112 | A special structure CapForRand is used that holds some 113 | extra info needed, the extra info being saved for 114 | efficiency reasons. 115 | */ 116 | 117 | /* 118 | theta, phi, angle in radians 119 | */ 120 | void CapForRand_from_thetaphi(struct CapForRand *rcap, 121 | long double theta, 122 | long double phi, 123 | long double angle_radians); 124 | /* 125 | ra, dec, angle in degrees 126 | */ 127 | void CapForRand_from_radec(struct CapForRand *rcap, 128 | long double ra, 129 | long double dec, 130 | long double angle_degrees); 131 | 132 | 133 | void genrand_cap_thetaphi(const struct CapForRand *rcap, 134 | int quadrant, 135 | long double *theta, 136 | long double *phi); 137 | void genrand_cap_radec(const struct CapForRand *rcap, 138 | int quadrant, 139 | long double *ra, 140 | long double *dec); 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /pymangle/stack.c: -------------------------------------------------------------------------------- 1 | // This file was auto-generated 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "stack.h" 9 | 10 | 11 | struct i64stack* i64stack_new(size_t num) { 12 | struct i64stack* stack = malloc(sizeof(struct i64stack)); 13 | if (stack == NULL) { 14 | fprintf(stderr,"Could not allocate struct i64stack\n"); 15 | return NULL; 16 | } 17 | 18 | stack->size = 0; 19 | stack->allocated_size = num; 20 | stack->push_realloc_style = STACK_PUSH_REALLOC_MULT; 21 | stack->push_initsize = STACK_PUSH_INITSIZE; 22 | stack->realloc_multval = STACK_PUSH_REALLOC_MULTVAL; 23 | 24 | if (num == 0) { 25 | stack->data = NULL; 26 | } else { 27 | stack->data = calloc(num, sizeof(int64_t)); 28 | if (stack->data == NULL) { 29 | free(stack); 30 | fprintf(stderr,"Could not allocate data for stack\n"); 31 | return NULL; 32 | } 33 | } 34 | 35 | return stack; 36 | } 37 | 38 | void i64stack_realloc(struct i64stack* stack, size_t newsize) { 39 | 40 | size_t oldsize = stack->allocated_size; 41 | if (newsize != oldsize) { 42 | size_t elsize = sizeof(int64_t); 43 | 44 | int64_t* newdata = realloc(stack->data, newsize*elsize); 45 | if (newdata == NULL) { 46 | fprintf(stderr,"failed to reallocate\n"); 47 | return; 48 | } 49 | 50 | if (newsize > stack->allocated_size) { 51 | // the allocated size is larger. make sure to initialize the new 52 | // memory region. This is the area starting from index [oldsize] 53 | size_t num_new_bytes = (newsize-oldsize)*elsize; 54 | memset(&newdata[oldsize], 0, num_new_bytes); 55 | } else if (stack->size > newsize) { 56 | // The viewed size is larger than the allocated size in this case, 57 | // we must set the size to the maximum it can be, which is the 58 | // allocated size 59 | stack->size = newsize; 60 | } 61 | 62 | stack->data = newdata; 63 | stack->allocated_size = newsize; 64 | } 65 | 66 | } 67 | void i64stack_resize(struct i64stack* stack, size_t newsize) { 68 | if (newsize > stack->allocated_size) { 69 | i64stack_realloc(stack, newsize); 70 | } 71 | 72 | stack->size = newsize; 73 | } 74 | 75 | void i64stack_clear(struct i64stack* stack) { 76 | stack->size=0; 77 | stack->allocated_size=0; 78 | free(stack->data); 79 | stack->data=NULL; 80 | } 81 | 82 | struct i64stack* i64stack_delete(struct i64stack* stack) { 83 | if (stack != NULL) { 84 | i64stack_clear(stack); 85 | free(stack); 86 | } 87 | return NULL; 88 | } 89 | 90 | void i64stack_push(struct i64stack* stack, int64_t val) { 91 | // see if we have already filled the available data vector 92 | // if so, reallocate to larger storage 93 | if (stack->size == stack->allocated_size) { 94 | 95 | size_t newsize; 96 | if (stack->allocated_size == 0) { 97 | newsize=stack->push_initsize; 98 | } else { 99 | // currenly we always use the multiplier reallocation method. 100 | if (stack->push_realloc_style != STACK_PUSH_REALLOC_MULT) { 101 | fprintf(stderr,"Currently only support push realloc style STACK_PUSH_REALLOC_MULT\n"); 102 | exit(EXIT_FAILURE); 103 | } 104 | // this will "floor" the size 105 | newsize = (size_t)(stack->allocated_size*stack->realloc_multval); 106 | // we want ceiling 107 | newsize++; 108 | } 109 | 110 | i64stack_realloc(stack, newsize); 111 | 112 | } 113 | 114 | stack->size++; 115 | stack->data[stack->size-1] = val; 116 | } 117 | 118 | int64_t i64stack_pop(struct i64stack* stack) { 119 | if (stack->size == 0) { 120 | return INT64_MAX; 121 | } 122 | 123 | int64_t val=stack->data[stack->size-1]; 124 | stack->size--; 125 | return val; 126 | 127 | } 128 | 129 | int __i64stack_compare_el(const void *a, const void *b) { 130 | int64_t temp = 131 | ( (int64_t) *( (int64_t*)a ) ) 132 | - 133 | ( (int64_t) *( (int64_t*)b ) ); 134 | if (temp > 0) 135 | return 1; 136 | else if (temp < 0) 137 | return -1; 138 | else 139 | return 0; 140 | } 141 | 142 | 143 | void i64stack_sort(struct i64stack* stack) { 144 | qsort(stack->data, stack->size, sizeof(int64_t), __i64stack_compare_el); 145 | } 146 | int64_t* i64stack_find(struct i64stack* stack, int64_t el) { 147 | return (int64_t*) bsearch(&el, stack->data, stack->size, sizeof(int64_t), __i64stack_compare_el); 148 | } 149 | -------------------------------------------------------------------------------- /tests/test_pymangle.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import numpy as np 4 | 5 | from pymangle import Mangle 6 | 7 | 8 | def test_standard_unpixelized(): 9 | 10 | """ 11 | a basic mask with standard header, no pixelization 12 | """ 13 | 14 | text = """3 polygons 15 | snapped 16 | polygon 0 ( 4 caps, 1 weight, 0 pixel, 0.101732020850849 str): 17 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 -0.2568551745226059 18 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 1.422618261740699 19 | 0.2367814257465403 0.0207156904863277 -0.9713420698132612 1 20 | 0.1515445107781580 0.0132584267126710 -0.9883615104677608 -1 21 | polygon 1 ( 4 caps, 1 weight, 0 pixel, 0.054242425709277 str): 22 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 -0.2568551745226059 23 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 1.5 24 | 0.1515445107781580 0.0132584267126710 -0.9883615104677608 1 25 | 0.1084526035253447 0.0094883733383393 -0.9940563382223196 -1 26 | polygon 2 ( 4 caps, 1 weight, 0 pixel, 0.302363552547417 str): 27 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 -0.2568551745226059 28 | 0.0871557427476579 -0.9961946980917455 0.0000000000000001 1.642787609686539 29 | 0.1084526035253447 0.0094883733383393 -0.9940563382223196 1 30 | -0.1084526035253447 -0.0094883733383393 -0.9940563382223196 -1\n""" 31 | 32 | with tempfile.TemporaryDirectory() as tmpdir: 33 | fname = os.path.join(tmpdir, 'test.ply') 34 | 35 | with open(fname, 'w') as fobj: 36 | fobj.write(text) 37 | 38 | m = Mangle(fname) 39 | ra, dec = m.genrand(3) 40 | 41 | 42 | def test_standard_pixelized(): 43 | """ 44 | standard header with pixel 45 | """ 46 | 47 | text = """3 polygons 48 | pixelization 9s 49 | snapped 50 | balkanized 51 | polygon 0 ( 4 caps, 1 weight, 87381 pixel, 0.000000000129178 str): 52 | 0.9999999991588143 0.0000410152374105 0.0000003490658505 0.998159921767887 53 | 0.9659041924494473 -0.2588997625991339 -0.0000627271332757 -0.9982831417433783 54 | -0.0000000000000000 1.0000000000000000 0.0000000000000000 1 55 | -0.0122715382857199 0.9999247018391445 0.0000000000000000 -1 56 | polygon 1 ( 4 caps, 1 weight, 87381 pixel, 0.000000067701897 str): 57 | -0.9647577286061684 -0.2584481766449109 0.0494678186661570 -1 58 | 0.2587637821532441 -0.9659406319340194 -0.0000249931148859 -0.9872370563678581 59 | -0.0122715382857199 0.9999247018391445 0.0000000000000000 -1 60 | 0.0000000000000000 0.0000000000000000 1.0000000000000000 0.00390625 61 | polygon 2 ( 5 caps, 1 weight, 87381 pixel, 0.000000311118396 str): 62 | -0.9647577286061684 -0.2584481766449109 0.0494678186661570 1 63 | 0.2587637821532441 -0.9659406319340194 -0.0000249931148859 -0.9872370563678581 64 | -0.9987456407765712 0.0000501504038277 0.0500713742045613 -1 65 | -0.0122715382857199 0.9999247018391445 0.0000000000000000 -1 66 | 0.0000000000000000 0.0000000000000000 1.0000000000000000 0.00390625\n""" # noqa 67 | 68 | with tempfile.TemporaryDirectory() as tmpdir: 69 | fname = os.path.join(tmpdir, 'test.ply') 70 | with open(fname, 'w') as fobj: 71 | fobj.write(text) 72 | 73 | _ = Mangle(fname) 74 | 75 | 76 | def test_nopixel(): 77 | """ 78 | standard header with no pixels in the header 79 | """ 80 | 81 | text = """3 polygons 82 | polygon 0 ( 4 caps, 1.0000000 weight, 0.0074088006084970 str): 83 | -0.06081623141086969775 -0.00532073080682029988 0.99813479842186692004 -1.00000000000000000000 84 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 -0.21198924639327809682 85 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 1.90996127087654299359 86 | -0.06515425057767830486 -0.00570025830607420042 0.99785892323860347908 1.00000000000000000000 87 | polygon 1 ( 4 caps, 1.0000000 weight, 0.0116826109771030 str): 88 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 1.88539361956466899883 89 | -0.06081623141086969775 -0.00532073080682029988 0.99813479842186692004 1.00000000000000000000 90 | -0.05387300028307989708 -0.00471327679489840033 0.99853667176641747183 -1.00000000000000000000 91 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 -0.21198924639327809682 92 | polygon 2 ( 4 caps, 1.0000000 weight, 0.0022611252679780 str): 93 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 1.50753836296070398149 94 | -0.05387300028307989708 -0.00471327679489840033 0.99853667176641747183 1.00000000000000000000 95 | -0.05213680212878300108 -0.00456137913876279982 0.99862953475457394426 -1.00000000000000000000 96 | 0.08715574274765790219 -0.99619469809174554520 0.00000000000000010000 -0.21198924639327809682\n""" # noqa 97 | 98 | with tempfile.TemporaryDirectory() as tmpdir: 99 | fname = os.path.join(tmpdir, 'test.ply') 100 | 101 | with open(fname, 'w') as fobj: 102 | fobj.write(text) 103 | 104 | m = Mangle(fname) 105 | 106 | n = 100 107 | ra, dec = m.genrand(n) 108 | assert np.all(m.contains(ra, dec)) 109 | 110 | ra, dec = m.genrand_range(n, 180, 190, -3.5, -3.2) 111 | assert np.all(m.contains(ra, dec)) 112 | 113 | ra, dec = m.genrand_range(n, 100, 250, -4, 0) 114 | assert np.all(m.contains(ra, dec)) 115 | 116 | 117 | def test_weight_only(): 118 | """ 119 | standard header with only weight in addition to caps 120 | """ 121 | 122 | text = """4 polygons 123 | polygon 1 ( 4 caps, 1 weight ): 124 | 0.0000000000 0.0000000000 1.0000000000 1.0174524064 125 | 0.0000000000 0.0000000000 1.0000000000 -0.8781306566 126 | 0.5000000000 0.8660254038 0.0000000000 1.0000000000 127 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 128 | polygon 2 ( 4 caps, 1 weight ): 129 | 0.0000000000 0.0000000000 1.0000000000 1.1218693434 130 | 0.0000000000 0.0000000000 1.0000000000 -1.0174524064 131 | -0.4617486132 0.8870108332 0.0000000000 1.0000000000 132 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 133 | polygon 3 ( 4 caps, 1 weight ): 134 | 0.0000000000 0.0000000000 1.0000000000 1.0348994967 135 | 0.0000000000 0.0000000000 1.0000000000 -0.9128442573 136 | -0.7933533403 -0.6087614290 0.0000000000 1.0000000000 137 | 0.7071067812 -0.7071067812 0.0000000000 -1.0000000000 138 | polygon 4 ( 4 caps, 1 weight ): 139 | 0.0000000000 0.0000000000 1.0000000000 0.3244097924 140 | 0.0000000000 0.0000000000 1.0000000000 -0.3053416295 141 | 0.3420201433 -0.9396926208 0.0000000000 1.0000000000 142 | 0.9396926208 -0.3420201433 0.0000000000 -1.0000000000\n""" 143 | 144 | with tempfile.TemporaryDirectory() as tmpdir: 145 | fname = os.path.join(tmpdir, 'test.ply') 146 | with open(fname, 'w') as fobj: 147 | fobj.write(text) 148 | 149 | m = Mangle(fname) 150 | ra, dec = m.genrand(3) 151 | 152 | 153 | def test_minimal_non_standard(): 154 | """ 155 | absolutely minimal header, non standard 156 | """ 157 | 158 | text = """4 polygons 159 | polygon 1 4 160 | 0.0000000000 0.0000000000 1.0000000000 1.0174524064 161 | 0.0000000000 0.0000000000 1.0000000000 -0.8781306566 162 | 0.5000000000 0.8660254038 0.0000000000 1.0000000000 163 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 164 | polygon 2 4 165 | 0.0000000000 0.0000000000 1.0000000000 1.1218693434 166 | 0.0000000000 0.0000000000 1.0000000000 -1.0174524064 167 | -0.4617486132 0.8870108332 0.0000000000 1.0000000000 168 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 169 | polygon 3 4 170 | 0.0000000000 0.0000000000 1.0000000000 1.0348994967 171 | 0.0000000000 0.0000000000 1.0000000000 -0.9128442573 172 | -0.7933533403 -0.6087614290 0.0000000000 1.0000000000 173 | 0.7071067812 -0.7071067812 0.0000000000 -1.0000000000 174 | polygon 4 4 175 | 0.0000000000 0.0000000000 1.0000000000 0.3244097924 176 | 0.0000000000 0.0000000000 1.0000000000 -0.3053416295 177 | 0.3420201433 -0.9396926208 0.0000000000 1.0000000000 178 | 0.9396926208 -0.3420201433 0.0000000000 -1.0000000000\n""" 179 | 180 | with tempfile.TemporaryDirectory() as tmpdir: 181 | fname = os.path.join(tmpdir, 'test.ply') 182 | with open(fname, 'w') as fobj: 183 | fobj.write(text) 184 | 185 | m = Mangle(fname) 186 | ra, dec = m.genrand(3) 187 | 188 | 189 | def test_minimal_non_standard_nopolycount(): 190 | """ 191 | absolutely minimal header, non standard, without even 192 | the polygon count 193 | """ 194 | 195 | text = """polygon 1 4 196 | 0.0000000000 0.0000000000 1.0000000000 1.0174524064 197 | 0.0000000000 0.0000000000 1.0000000000 -0.8781306566 198 | 0.5000000000 0.8660254038 0.0000000000 1.0000000000 199 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 200 | polygon 2 4 201 | 0.0000000000 0.0000000000 1.0000000000 1.1218693434 202 | 0.0000000000 0.0000000000 1.0000000000 -1.0174524064 203 | -0.4617486132 0.8870108332 0.0000000000 1.0000000000 204 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 205 | polygon 3 4 206 | 0.0000000000 0.0000000000 1.0000000000 1.0348994967 207 | 0.0000000000 0.0000000000 1.0000000000 -0.9128442573 208 | -0.7933533403 -0.6087614290 0.0000000000 1.0000000000 209 | 0.7071067812 -0.7071067812 0.0000000000 -1.0000000000 210 | polygon 4 4 211 | 0.0000000000 0.0000000000 1.0000000000 0.3244097924 212 | 0.0000000000 0.0000000000 1.0000000000 -0.3053416295 213 | 0.3420201433 -0.9396926208 0.0000000000 1.0000000000 214 | 0.9396926208 -0.3420201433 0.0000000000 -1.0000000000\n""" 215 | 216 | with tempfile.TemporaryDirectory() as tmpdir: 217 | fname = os.path.join(tmpdir, 'test.ply') 218 | 219 | with open(fname, 'w') as fobj: 220 | fobj.write(text) 221 | 222 | m = Mangle(fname) 223 | ra, dec = m.genrand(3) 224 | 225 | 226 | def test_extra_lines(): 227 | """ 228 | extra line at the end 229 | """ 230 | 231 | text = """polygon 1 4 232 | 0.0000000000 0.0000000000 1.0000000000 1.0174524064 233 | 0.0000000000 0.0000000000 1.0000000000 -0.8781306566 234 | 0.5000000000 0.8660254038 0.0000000000 1.0000000000 235 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 236 | polygon 2 4 237 | 0.0000000000 0.0000000000 1.0000000000 1.1218693434 238 | 0.0000000000 0.0000000000 1.0000000000 -1.0174524064 239 | -0.4617486132 0.8870108332 0.0000000000 1.0000000000 240 | -0.6427876097 0.7660444431 0.0000000000 -1.0000000000 241 | polygon 3 4 242 | 0.0000000000 0.0000000000 1.0000000000 1.0348994967 243 | 0.0000000000 0.0000000000 1.0000000000 -0.9128442573 244 | -0.7933533403 -0.6087614290 0.0000000000 1.0000000000 245 | 0.7071067812 -0.7071067812 0.0000000000 -1.0000000000 246 | polygon 4 4 247 | 0.0000000000 0.0000000000 1.0000000000 0.3244097924 248 | 0.0000000000 0.0000000000 1.0000000000 -0.3053416295 249 | 0.3420201433 -0.9396926208 0.0000000000 1.0000000000 250 | 0.9396926208 -0.3420201433 0.0000000000 -1.0000000000\n 251 | \n""" 252 | 253 | with tempfile.TemporaryDirectory() as tmpdir: 254 | fname = os.path.join(tmpdir, 'test.ply') 255 | with open(fname, 'w') as fobj: 256 | fobj.write(text) 257 | 258 | m = Mangle(fname) 259 | ra, dec = m.genrand(3) 260 | -------------------------------------------------------------------------------- /pymangle/cap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "cap.h" 6 | #include "point.h" 7 | #include "defs.h" 8 | 9 | /* 10 | Cap code 11 | */ 12 | 13 | void cap_set(struct Cap* self, 14 | long double x, 15 | long double y, 16 | long double z, 17 | long double cm) 18 | { 19 | self->x=x; 20 | self->y=y; 21 | self->z=z; 22 | self->cm=cm; 23 | } 24 | 25 | 26 | int read_cap(FILE* fptr, struct Cap* self) 27 | { 28 | int status=1, nres=0; 29 | 30 | nres = fscanf(fptr, 31 | "%Lf %Lf %Lf %Lf",&self->x,&self->y,&self->z,&self->cm); 32 | 33 | if (nres != 4) { 34 | status=0; 35 | wlog("Failed to read cap\n"); 36 | } 37 | 38 | return status; 39 | } 40 | 41 | void print_cap(FILE* fptr, struct Cap* self) 42 | { 43 | if (!self) 44 | return; 45 | 46 | fprintf(fptr, " %.18Lg %.18Lg %.18Lg %.18Lg\n", 47 | self->x, self->y, self->z, self->cm); 48 | } 49 | 50 | void snprint_cap(const struct Cap* self, char *buff, size_t n) 51 | { 52 | if (!self) 53 | return; 54 | 55 | snprintf(buff, 56 | n, 57 | "%.18Lg %.18Lg %.18Lg %.18Lg", 58 | self->x, 59 | self->y, 60 | self->z, 61 | self->cm); 62 | } 63 | 64 | 65 | 66 | int is_in_cap(struct Cap* cap, struct Point* pt) 67 | { 68 | int incap=0; 69 | long double cdotm=0; 70 | 71 | // cm = 1-cos(theta) where theta is the cap opening angle 72 | cdotm = 1.0 - cap->x*pt->x - cap->y*pt->y - cap->z*pt->z; 73 | if (cap->cm < 0.0) { 74 | incap = cdotm > (-cap->cm); 75 | } else { 76 | incap = cdotm < cap->cm; 77 | } 78 | 79 | return incap; 80 | } 81 | 82 | /* 83 | 84 | CapVec code 85 | 86 | */ 87 | 88 | struct CapVec* capvec_new(void) 89 | { 90 | struct CapVec* self=NULL; 91 | 92 | self=calloc(1, sizeof(struct CapVec)); 93 | if (self == NULL) { 94 | return NULL; 95 | } 96 | 97 | self->capacity = CAPVEC_INITCAP; 98 | self->size = 0; 99 | 100 | self->data = calloc(self->capacity, sizeof(struct Cap)); 101 | if (self->data == NULL) { 102 | free(self); 103 | return NULL; 104 | } 105 | return self; 106 | } 107 | 108 | struct CapVec* capvec_zeros(size_t n) 109 | { 110 | struct CapVec* self=NULL; 111 | 112 | self=calloc(1, sizeof(struct CapVec)); 113 | if (self == NULL) { 114 | return NULL; 115 | } 116 | 117 | self->capacity = n; 118 | self->size = n; 119 | 120 | self->data = calloc(self->capacity, sizeof(struct Cap)); 121 | if (self->data == NULL) { 122 | free(self); 123 | return NULL; 124 | } 125 | 126 | return self; 127 | } 128 | 129 | struct CapVec* capvec_free(struct CapVec* self) 130 | { 131 | if (self != NULL) { 132 | if (self->data) { 133 | free(self->data); 134 | self->data=NULL; 135 | } 136 | free(self); 137 | self=NULL; 138 | } 139 | return self; 140 | } 141 | 142 | 143 | static int capvec_realloc(struct CapVec* self, size_t new_capacity) 144 | { 145 | size_t old_capacity=self->capacity; 146 | 147 | if (new_capacity < old_capacity) { 148 | self->size=new_capacity; 149 | } 150 | 151 | if (new_capacity < 1) new_capacity=1; 152 | 153 | size_t sizeof_type = sizeof(struct Cap); 154 | 155 | if (new_capacity != old_capacity) { 156 | self->data = realloc(self->data, new_capacity*sizeof_type); 157 | if (!self->data) { 158 | fprintf(stderr, "failed to reallocate\n"); 159 | return 0; 160 | } 161 | if (new_capacity > old_capacity) { 162 | // zero out additional elements if new capacity is larger 163 | size_t num_new_bytes = (new_capacity-old_capacity)*sizeof_type; 164 | memset(self->data + old_capacity, 0, num_new_bytes); 165 | } 166 | 167 | self->capacity = new_capacity; 168 | } 169 | 170 | return 1; 171 | } 172 | 173 | int capvec_reserve(struct CapVec* self, size_t new_capacity) 174 | { 175 | int status=1; 176 | if (new_capacity > self->capacity) { 177 | status=capvec_realloc(self, new_capacity); 178 | } 179 | return status; 180 | } 181 | 182 | int capvec_resize(struct CapVec* self, size_t new_size) 183 | { 184 | int status=1; 185 | 186 | // if new size is smaller, we will just leave junk in the 187 | // unused slots, which is OK since they are not "visible". 188 | // 189 | // If size is larger, only new capacity is zerod by reserve 190 | // so we want to zero any elements up to current capacity 191 | 192 | if (new_size > self->size) { 193 | if (self->size < self->capacity) { 194 | size_t n_to_zero=self->capacity-self->size; 195 | memset(&self->data[self->size], 196 | 0, 197 | n_to_zero*sizeof(struct Cap)); 198 | } 199 | } 200 | 201 | // get at least new_size capacity 202 | status=capvec_reserve(self, new_size); 203 | if (status) { 204 | self->size=new_size; 205 | } 206 | 207 | return status; 208 | } 209 | 210 | int capvec_clear(struct CapVec* self) 211 | { 212 | int status=0; 213 | status=capvec_realloc(self, CAPVEC_INITCAP); 214 | 215 | if (status) { 216 | self->size=0; 217 | } 218 | 219 | return status; 220 | } 221 | 222 | int capvec_push(struct CapVec* self, const struct Cap* cap) 223 | { 224 | int status=1; 225 | if (self->size == self->capacity) { 226 | size_t new_capacity=0; 227 | if (self->capacity==0) { 228 | new_capacity=CAPVEC_INITCAP; 229 | } else { 230 | new_capacity = self->capacity*CAPVEC_PUSH_REALLOC_MULTVAL; 231 | } 232 | status=capvec_realloc(self, new_capacity); 233 | } 234 | 235 | if (status) { 236 | self->size++; 237 | self->data[self->size-1] = *cap; 238 | } 239 | 240 | return status; 241 | } 242 | 243 | struct Cap capvec_pop(struct CapVec* self) 244 | { 245 | size_t index=0; 246 | 247 | if (self->size > 0) { 248 | index = self->size-1; 249 | self->size--; 250 | } else { 251 | fprintf(stderr, 252 | "CapVecError: attempt to pop from empty vector, returning garbage\n"); 253 | } 254 | 255 | return self->data[index]; 256 | } 257 | 258 | struct CapVec* capvec_copy(const struct CapVec* self) 259 | { 260 | struct CapVec * cap_vec=NULL; 261 | if (!self) { 262 | return NULL; 263 | } 264 | 265 | cap_vec = capvec_zeros(self->size); 266 | memcpy(cap_vec->data, 267 | self->data, 268 | self->size*sizeof(struct Cap)); 269 | 270 | return cap_vec; 271 | } 272 | 273 | void capvec_min_cm(const struct CapVec* self, 274 | size_t *index, 275 | long double* cm_min) 276 | { 277 | struct Cap* cap=NULL; 278 | size_t i=0; 279 | double cm=0; 280 | 281 | *cm_min = 2.; 282 | for (i = 0; i < self->size; i++) { 283 | cap = &self->data[i]; 284 | 285 | if (cap->cm >= 0.0) { 286 | cm = cap->cm; 287 | } else { 288 | cm = 2. + cap->cm; 289 | } 290 | 291 | if (cm <= *cm_min) { 292 | *index= i; 293 | *cm_min = cm; 294 | } 295 | } 296 | 297 | } 298 | 299 | 300 | /* 301 | 302 | CapForRand code 303 | 304 | */ 305 | 306 | void CapForRand_from_thetaphi(struct CapForRand *rcap, 307 | long double theta, 308 | long double phi, 309 | long double angle_radians) 310 | { 311 | rcap->theta=theta; 312 | rcap->phi=phi; 313 | rcap->cos_theta = cosl(theta); 314 | rcap->sin_theta = sinl(theta); 315 | rcap->cos_phi = cosl(phi); 316 | rcap->sin_phi = sinl(phi); 317 | rcap->angle = angle_radians; 318 | } 319 | void CapForRand_from_radec(struct CapForRand *rcap, 320 | long double ra, 321 | long double dec, 322 | long double angle_degrees) 323 | { 324 | long double theta, phi; 325 | thetaphi_from_radec(ra, dec, &theta, &phi); 326 | 327 | CapForRand_from_thetaphi(rcap, theta, phi, angle_degrees*D2R); 328 | } 329 | 330 | void genrand_cap_thetaphi(const struct CapForRand *rcap, 331 | int quadrant, 332 | long double *theta, 333 | long double *phi) 334 | { 335 | long double rand_r, rand_posangle, 336 | sinr, cosr, cospsi, sintheta, costheta, 337 | cosDphi, Dphi; 338 | 339 | // uniform in opening angle squared 340 | rand_r = (long double) ( sqrt(drand48())*rcap->angle ); 341 | 342 | rand_posangle = (long double) ( drand48()*2*M_PI ); 343 | switch (quadrant) { 344 | case 1: 345 | rand_posangle *= 0.25; // scale back to range pi/2 346 | break; 347 | case 2: 348 | rand_posangle *= 0.25; // scale back to range pi/2 349 | rand_posangle += 0.5*M_PI; // translate to [pi/2,pi] 350 | break; 351 | case 3: 352 | rand_posangle *= 0.25; // scale back to range pi/2 353 | rand_posangle += M_PI; // translate to [pi, 3*pi/2] 354 | break; 355 | case 4: 356 | rand_posangle *= 0.25; // scale back to range pi/2 357 | rand_posangle += 1.5*M_PI; // translate to [3*pi/2, 2*pi] 358 | break; 359 | default: 360 | // keep full cap 361 | break; 362 | } 363 | 364 | sinr = sinl(rand_r); 365 | cosr = cosl(rand_r); 366 | 367 | cospsi = cosl(rand_posangle); 368 | costheta = rcap->cos_theta*cosr + rcap->sin_theta*sinr*cospsi; 369 | if (costheta < -1.) { 370 | costheta = -1.; 371 | } else if (costheta > 1.) { 372 | costheta = 1.; 373 | } 374 | 375 | (*theta) = acosl(costheta); 376 | sintheta = sinl(*theta); 377 | 378 | cosDphi = (cosr - rcap->cos_theta*costheta)/(rcap->sin_theta*sintheta); 379 | 380 | if (cosDphi < -1.) { 381 | cosDphi = -1.; 382 | } else if (cosDphi > 1.) { 383 | cosDphi = 1.; 384 | } 385 | Dphi = acosl(cosDphi); 386 | 387 | if (rand_posangle > M_PI) { 388 | (*phi) = rcap->phi + Dphi; 389 | } else { 390 | (*phi) = rcap->phi - Dphi; 391 | } 392 | } 393 | 394 | void genrand_cap_radec(const struct CapForRand *rcap, 395 | int quadrant, 396 | long double *ra, 397 | long double *dec) 398 | { 399 | long double theta, phi; 400 | int quadrant_thetaphi; 401 | 402 | switch (quadrant) { 403 | case 1: 404 | quadrant_thetaphi = 4; 405 | break; 406 | case 2: 407 | quadrant_thetaphi = 1; 408 | break; 409 | case 3: 410 | quadrant_thetaphi = 2; 411 | break; 412 | case 4: 413 | quadrant_thetaphi = 3; 414 | break; 415 | default: 416 | quadrant_thetaphi=quadrant; 417 | break; 418 | } 419 | 420 | 421 | genrand_cap_thetaphi(rcap, quadrant_thetaphi, &theta, &phi); 422 | radec_from_thetaphi(theta, phi, ra, dec); 423 | } 424 | -------------------------------------------------------------------------------- /pymangle/mangle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mangle.h" 5 | #include "polygon.h" 6 | #include "defs.h" 7 | 8 | 9 | 10 | struct MangleMask* mangle_new() 11 | { 12 | struct MangleMask* self=NULL; 13 | 14 | self = calloc(1, sizeof(struct MangleMask)); 15 | if (self == NULL) { 16 | wlog("Failed to allocate MangleMask\n"); 17 | return NULL; 18 | } 19 | 20 | mangle_clear(self); 21 | return self; 22 | } 23 | 24 | 25 | 26 | struct MangleMask* mangle_free(struct MangleMask* self) 27 | { 28 | mangle_clear(self); 29 | free(self); 30 | return NULL; 31 | } 32 | void mangle_clear(struct MangleMask* self) 33 | { 34 | if (self != NULL) { 35 | 36 | free(self->filename); 37 | self->filename=NULL; 38 | 39 | memset(self->weightfile, 0, sizeof(self->weightfile)); 40 | 41 | self->poly_vec = polyvec_free(self->poly_vec); 42 | self->pixel_list_vec = PixelListVec_free(self->pixel_list_vec); 43 | 44 | self->pixelres=-1; 45 | self->maxpix=-1; 46 | self->pixeltype='u'; 47 | } 48 | } 49 | void mangle_set_verbosity(struct MangleMask* self, int verbosity) 50 | { 51 | if (self) { 52 | self->verbose=verbosity; 53 | } 54 | } 55 | 56 | void mangle_print(FILE* fptr, struct MangleMask* self, int verbosity) 57 | { 58 | if (!self || verbosity == 0) 59 | return; 60 | 61 | size_t npoly; 62 | size_t npix; 63 | 64 | npoly = (self->poly_vec != NULL) ? self->poly_vec->size : 0; 65 | npix = (self->pixel_list_vec != NULL) ? self->pixel_list_vec->size : 0; 66 | 67 | fprintf(fptr, 68 | "Mangle\n" 69 | "\tfile: %s\n" 70 | "\tarea: %Lg sqdeg\n" 71 | "\tnpoly: %ld\n" 72 | "\tpixeltype: '%c'\n" 73 | "\tpixelres: %ld\n" 74 | "\tnpix: %ld\n" 75 | "\tsnapped: %d\n" 76 | "\tbalkanized: %d\n" 77 | "\tweightfile: %s\n" 78 | "\tverbose: %d\n", 79 | self->filename, self->total_area*R2D*R2D, 80 | npoly, self->pixeltype, self->pixelres, npix, 81 | self->snapped, self->balkanized, self->weightfile, 82 | self->verbose); 83 | 84 | if (verbosity > 1) { 85 | print_polygons(fptr,self->poly_vec); 86 | } 87 | } 88 | 89 | 90 | int mangle_read(struct MangleMask* self, const char* filename) 91 | { 92 | int status=1; 93 | FILE *fptr=NULL; 94 | 95 | mangle_clear(self); 96 | self->real = 10; // default 97 | self->filename=strdup(filename); 98 | 99 | fptr = fopen(filename,"r"); 100 | if (fptr == NULL) { 101 | wlog("Failed to open file for reading: %s\n",filename); 102 | status=0; 103 | goto _mangle_read_bail; 104 | } 105 | 106 | if (!mangle_read_header(self, fptr)) { 107 | status=0; 108 | goto _mangle_read_bail; 109 | } 110 | 111 | if (self->verbose) 112 | wlog("reading %ld polygons\n", self->npoly); 113 | self->poly_vec = read_polygons(fptr, self->npoly); 114 | if (!self->poly_vec) { 115 | status=0; 116 | goto _mangle_read_bail; 117 | } 118 | 119 | mangle_calc_area_and_maxpix(self); 120 | 121 | if (!set_pixel_map(self)) { 122 | status=0; 123 | goto _mangle_read_bail; 124 | } 125 | 126 | _mangle_read_bail: 127 | if (fptr != NULL) { 128 | fclose(fptr); fptr=NULL; 129 | } 130 | return status; 131 | } 132 | 133 | static size_t count_polygons(FILE* fptr) 134 | { 135 | size_t i=0; 136 | char *linebuf=NULL; 137 | size_t len=0, npoly=0; 138 | ssize_t nread=0; 139 | 140 | char keyword[9] = "polygon "; 141 | size_t klen = strlen(keyword); 142 | size_t tocomp=0; 143 | 144 | rewind(fptr); 145 | while ( (nread=getline(&linebuf, &len, fptr)) != -1 ) { 146 | if (len < klen) { 147 | tocomp=len; 148 | } else { 149 | tocomp=klen; 150 | } 151 | 152 | if (0==strncmp(keyword,linebuf,tocomp)) { 153 | npoly += 1; 154 | } 155 | } 156 | 157 | free(linebuf); 158 | rewind(fptr); 159 | 160 | return npoly; 161 | } 162 | 163 | // npoly 164 | // snapped 165 | // balkanized 166 | // pixelres 167 | // pixeltype 168 | int mangle_read_header(struct MangleMask* self, FILE* fptr) 169 | { 170 | int status=1; 171 | 172 | if (2 != fscanf(fptr,"%ld %s", &self->npoly, self->buff)) { 173 | self->npoly=count_polygons(fptr); 174 | //status = 0; 175 | //wlog("Could not read number of polygons"); 176 | //goto _read_header_bail; 177 | } else { 178 | if (0 != strcmp(self->buff,"polygons")) { 179 | status = 0; 180 | wlog("Expected keyword 'polygons' but got '%s'", self->buff); 181 | goto _read_header_bail; 182 | } 183 | } 184 | 185 | if (self->verbose) 186 | wlog("Expect %ld polygons\n", self->npoly); 187 | 188 | // get some metadata 189 | if (1 != fscanf(fptr,"%s", self->buff) ) { 190 | status=0; 191 | wlog("Error reading header keyword"); 192 | goto _read_header_bail; 193 | } 194 | 195 | while (0 != strcmp(self->buff,"polygon")) { 196 | if (0 == strcmp(self->buff,"snapped")) { 197 | 198 | if (self->verbose) 199 | wlog("\tpolygons are snapped\n"); 200 | self->snapped=1; 201 | 202 | } else if (0 == strcmp(self->buff,"balkanized")) { 203 | 204 | if (self->verbose) 205 | wlog("\tpolygons are balkanized\n"); 206 | self->balkanized=1; 207 | 208 | } else if (0 == strcmp(self->buff,"real")) { 209 | 210 | if (1 != fscanf(fptr,"%d", &self->real)) { 211 | status=0; 212 | wlog("Error reading real value"); 213 | goto _read_header_bail; 214 | } 215 | if (self->verbose) 216 | wlog("\treal: %d\n",self->real); 217 | if ((self->real != 8) && (self->real != 10)) { 218 | status=0; 219 | wlog("Illegal real value (must be 8 or 10)"); 220 | goto _read_header_bail; 221 | } 222 | 223 | } else if (0 == strcmp(self->buff,"pixelization")) { 224 | 225 | // read the pixelization description, e.g. 9s 226 | if (1 != fscanf(fptr,"%s", self->buff)) { 227 | status=0; 228 | wlog("Error reading pixelization scheme"); 229 | goto _read_header_bail; 230 | } 231 | if (self->verbose) 232 | wlog("\tpixelization scheme: '%s'\n", self->buff); 233 | 234 | 235 | if (!pixel_parse_scheme(self->buff, &self->pixelres, &self->pixeltype)) { 236 | goto _read_header_bail; 237 | } 238 | if (self->verbose) { 239 | wlog("\t\tscheme: '%c'\n", self->pixeltype); 240 | wlog("\t\tres: %ld\n", self->pixelres); 241 | } 242 | 243 | } else { 244 | status=0; 245 | wlog("Got unexpected header keyword: '%s'", self->buff); 246 | goto _read_header_bail; 247 | } 248 | if (1 != fscanf(fptr,"%s", self->buff) ) { 249 | status=0; 250 | wlog("Error reading header keyword"); 251 | goto _read_header_bail; 252 | } 253 | } 254 | 255 | 256 | _read_header_bail: 257 | return status; 258 | } 259 | 260 | int mangle_read_weights(struct MangleMask* self, const char* weightfile) 261 | { 262 | int status=1; 263 | FILE *wfptr; 264 | int64 i; 265 | 266 | long double *weight_new; 267 | long double test; 268 | 269 | struct Polygon *ply=NULL; 270 | 271 | 272 | 273 | if ((wfptr = fopen(weightfile,"r")) == NULL) { 274 | wlog("Failed to open file for reading: %s\n",weightfile); 275 | status=0; 276 | goto _mangle_readweight_bail; 277 | } 278 | 279 | // allocate memory 280 | if ((weight_new = (long double *)calloc(self->npoly,sizeof(long double))) == NULL) { 281 | wlog("Failed to allocate memory for reading %s\n",weightfile); 282 | fclose(wfptr); 283 | status=0; 284 | goto _mangle_readweight_bail; 285 | } 286 | 287 | // read in the lines 288 | 289 | for (i=0;inpoly;i++) { 290 | if (1 != fscanf(wfptr,"%Lf",&(weight_new[i]))) { 291 | wlog("Number of weights in weightfile %s less than number of polygons (%ld)\n",weightfile,self->npoly); 292 | fclose(wfptr); 293 | status=0; 294 | free(weight_new); 295 | goto _mangle_readweight_bail; 296 | } 297 | } 298 | 299 | // are there any extra lines? produce error. 300 | if (fscanf(wfptr,"%Lf",&test) == 1) { 301 | wlog("Number of weights in weightfile %s greater than number of polygons (%ld)\n",weightfile,self->npoly); 302 | fclose(wfptr); 303 | status=0; 304 | free(weight_new); 305 | goto _mangle_readweight_bail; 306 | } 307 | 308 | // and copy... 309 | for (i=0;inpoly;i++) { 310 | ply = &self->poly_vec->data[i]; 311 | ply->weight = weight_new[i]; 312 | } 313 | 314 | // free memory 315 | free(weight_new); 316 | 317 | 318 | // and because it all worked we can set the filename 319 | snprintf(self->weightfile,_MANGLE_MAX_FILELEN,"%s",weightfile); 320 | 321 | _mangle_readweight_bail: 322 | if (wfptr != NULL) { 323 | fclose(wfptr); 324 | } 325 | return status; 326 | } 327 | 328 | int mangle_set_weights(struct MangleMask* self, long double *weights) { 329 | int status=1; 330 | int64 i; 331 | struct Polygon *ply=NULL; 332 | 333 | for (i=0;inpoly;i++) { 334 | ply = &self->poly_vec->data[i]; 335 | ply->weight = weights[i]; 336 | } 337 | 338 | memset(self->weightfile, 0, sizeof(self->weightfile)); 339 | 340 | return status; 341 | } 342 | 343 | void mangle_calc_area_and_maxpix(struct MangleMask* self) 344 | { 345 | struct Polygon* ply=NULL; 346 | 347 | if (self) { 348 | self->total_area = 0.0; 349 | if (self->poly_vec) { 350 | size_t i=0; 351 | for (i=0; ipoly_vec->size; i++) { 352 | ply = &self->poly_vec->data[i]; 353 | self->total_area += ply->area; 354 | 355 | if (ply->pixel_id > self->maxpix) { 356 | self->maxpix = ply->pixel_id; 357 | } 358 | 359 | } 360 | } 361 | } 362 | } 363 | 364 | int set_pixel_map(struct MangleMask *self) 365 | { 366 | int status=1; 367 | struct Polygon* ply=NULL; 368 | int64 ipoly=0; 369 | 370 | if (self->pixelres >= 0) { 371 | if (self->verbose) { 372 | fprintf(stderr,"Allocating %ld in PixelListVec\n", 373 | self->maxpix+1); 374 | } 375 | self->pixel_list_vec = PixelListVec_new(self->maxpix+1); 376 | if (self->pixel_list_vec == NULL) { 377 | status = 0; 378 | goto _set_pixel_map_errout; 379 | } else { 380 | 381 | if (self->verbose) 382 | fprintf(stderr,"Filling pixel map\n"); 383 | 384 | for (ipoly=0; ipolypoly_vec->size; ipoly++) { 385 | ply=&self->poly_vec->data[ipoly]; 386 | i64stack_push(self->pixel_list_vec->data[ply->pixel_id], ipoly); 387 | if (self->verbose > 2) { 388 | fprintf(stderr, 389 | "Adding poly %ld to pixel map at %ld (%ld)\n", 390 | ipoly,ply->pixel_id, 391 | self->pixel_list_vec->data[ply->pixel_id]->size); 392 | } 393 | } 394 | } 395 | } 396 | _set_pixel_map_errout: 397 | 398 | return status; 399 | 400 | } 401 | 402 | int mangle_polyid_and_weight(struct MangleMask *self, 403 | struct Point *pt, 404 | int64 *poly_id, 405 | long double *weight) 406 | { 407 | if (self->pixeltype == 'u') { 408 | return mangle_polyid_and_weight_nopix(self,pt,poly_id,weight); 409 | } else { 410 | return mangle_polyid_and_weight_pix(self,pt,poly_id,weight); 411 | } 412 | } 413 | 414 | int mangle_polyid_and_weight_nopix(struct MangleMask *self, 415 | struct Point *pt, 416 | int64 *poly_id, 417 | long double *weight) 418 | { 419 | size_t i=0; 420 | struct Polygon* ply=NULL; 421 | 422 | *poly_id=-1; 423 | *weight=0.0; 424 | 425 | for (i=0; ipoly_vec->size; i++) { 426 | ply = &self->poly_vec->data[i]; 427 | if (is_in_poly(ply, pt)) { 428 | *poly_id=ply->poly_id; 429 | *weight=ply->weight; 430 | break; 431 | } 432 | } 433 | return 1; 434 | } 435 | int mangle_polyid_and_weight_pix(struct MangleMask *self, 436 | struct Point *pt, 437 | int64 *poly_id, 438 | long double *weight) 439 | { 440 | int status=1; 441 | size_t i=0; 442 | int64 pix=0, ipoly=0; 443 | struct i64stack* pstack=NULL; 444 | struct Polygon* ply=NULL; 445 | 446 | *poly_id=-1; 447 | *weight=0.0; 448 | 449 | if (self->pixeltype == 's') { 450 | pix = get_pixel_simple(self->pixelres, pt); 451 | if (pix < self->pixel_list_vec->size) { 452 | // this is a stack holding indices into the polygon vector 453 | pstack = self->pixel_list_vec->data[pix]; 454 | 455 | for (i=0; isize; i++) { 456 | ipoly = pstack->data[i]; 457 | ply = &self->poly_vec->data[ipoly]; 458 | 459 | if (is_in_poly(ply, pt)) { 460 | *poly_id=ply->poly_id; 461 | *weight=ply->weight; 462 | break; 463 | } 464 | } 465 | } 466 | } else { 467 | status=0; 468 | wlog("Unsupported pixelization scheme: '%c'",self->pixeltype); 469 | } 470 | return status; 471 | } 472 | 473 | 474 | -------------------------------------------------------------------------------- /pymangle/mangle.py: -------------------------------------------------------------------------------- 1 | """ 2 | classes 3 | Mangle: 4 | A class to work with mangle polygons 5 | 6 | functions: 7 | genrand_cap: 8 | Generate random points in a spherical cap 9 | """ 10 | # we over-ride the init to deal with verbose 11 | # we over-ride point checking codes force inputs to be arrays 12 | # 13 | # note we do *not* over-ride the genrand* functions, 14 | # as they perform conversions as needed 15 | # 16 | # we also grab the doc strings from the C code as needed, 17 | # only writing new ones in the over-ridden methods 18 | 19 | from __future__ import print_function, absolute_import 20 | 21 | from numpy import array, longdouble 22 | from . import _mangle 23 | 24 | 25 | def genrand_cap(nrand, ra, dec, angle_degrees, quadrant=-1): 26 | """ 27 | generate random points in a spherical cap 28 | 29 | parameters 30 | ---------- 31 | nrand: scalar 32 | Number of random points to generate 33 | ra: scalar 34 | ra center of cap in degrees 35 | dec: scalar 36 | dec center of cap in degrees 37 | angle_degrees: scalar 38 | opening angle of cap in degrees 39 | quadrant: scalar 40 | Quadrant in which to generate the points. Set to 41 | 1,2,3,4 to specify a quadrant, anything else to 42 | generate the full cap. Default -1 (full cap) 43 | 44 | returns 45 | ------- 46 | ra,dec: arrays 47 | the random points 48 | """ 49 | return _mangle.genrand_cap(nrand, ra, dec, angle_degrees, quadrant) 50 | 51 | 52 | class Mangle(_mangle.Mangle): 53 | __doc__ = _mangle.Mangle.__doc__ 54 | 55 | def __init__(self, filename, verbose=False): 56 | if verbose: 57 | verb = 1 58 | else: 59 | verb = 0 60 | 61 | super(Mangle, self).__init__(filename, verb) 62 | 63 | def read_weights(self, weightfile): 64 | """ 65 | Read weights from a file with one weight on each line. 66 | 67 | parameters 68 | ---------- 69 | weightfile: ascii file with one weight on each line. 70 | Must have same number of weights as npoly. 71 | 72 | output 73 | ------ 74 | none 75 | """ 76 | 77 | super(Mangle, self).read_weights(weightfile) 78 | 79 | def polyid_and_weight(self, ra, dec): 80 | """ 81 | Check points against mask, returning (poly_id,weight). 82 | 83 | parameters 84 | ---------- 85 | ra: scalar or array 86 | Right ascension in degrees. Can be an array. 87 | dec: scalar or array 88 | Declination in degrees. Can be an array. 89 | 90 | output 91 | ------ 92 | polyd,weight tuple of arrays 93 | """ 94 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False, order='C') 95 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False, order='C') 96 | return super(Mangle, self).polyid_and_weight(ra, dec) 97 | 98 | def polyid(self, ra, dec): 99 | """ 100 | Check points against mask, returning the polygon id or -1. 101 | 102 | parameters 103 | ---------- 104 | ra: scalar or array 105 | Right ascension in degrees. Can be an array. 106 | dec: scalar or array 107 | Declination in degrees. Can be an array. 108 | 109 | output 110 | ------ 111 | Array of poly ids 112 | """ 113 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False, order='C') 114 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False, order='C') 115 | return super(Mangle, self).polyid(ra, dec) 116 | 117 | def weight(self, ra, dec): 118 | """ 119 | Check points against mask, returning the weight or 0. 120 | 121 | parameters 122 | ---------- 123 | ra: scalar or array 124 | Right ascension in degrees. Can be an array. 125 | dec: scalar or array 126 | Declination in degrees. Can be an array. 127 | 128 | output 129 | ------ 130 | Array of weights 131 | """ 132 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False, order='C') 133 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False, order='C') 134 | return super(Mangle, self).weight(ra, dec) 135 | 136 | def contains(self, ra, dec): 137 | """ 138 | Check points against mask, returning 1 if contained 0 if not 139 | 140 | parameters 141 | ---------- 142 | ra: scalar or array 143 | Right ascension in degrees. Can be an array. 144 | dec: scalar or array 145 | Declination in degrees. Can be an array. 146 | 147 | output 148 | ------ 149 | Array of zeros or ones 150 | """ 151 | # we specify order to force contiguous 152 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False, order='C') 153 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False, order='C') 154 | return super(Mangle, self).contains(ra, dec) 155 | 156 | def check_quadrants(self, 157 | ra, 158 | dec, 159 | angle_degrees, 160 | density=10.0*60.0**2, 161 | max_masked_fraction=0.05): 162 | """ 163 | Check points quadrants of the spherical cap against the mask 164 | using random points 165 | 166 | If more than a certain fraction of the random points is masked, the 167 | quadrant is considered masked. The sensitivity can be adjusted 168 | by using a higher density of random points. 169 | 170 | parameters 171 | ---------- 172 | ra: scalar or array 173 | Right ascension in degrees. Can be an array. 174 | dec: scalar or array 175 | Declination in degrees. Can be an array. 176 | angle_degrees: scalar or array 177 | radius angle of cap in degrees. Can be an array. 178 | should be same length as ra,dec 179 | density: scalar 180 | Density for random points placed in cap per square 181 | degree. Default is 36000.0, which corresponds to 182 | 10 per square arcminute. 183 | max_masked_fraction: scalar 184 | If more than this fraction of random points in the 185 | quadrant are masked, the quadrant is considered bad 186 | 187 | output 188 | ------ 189 | maskflags: array 190 | 2**0 is set if center of cap is inside the map 191 | 2**1 is set if first quadrant is OK 192 | 2**2 is set if second quadrant is OK 193 | 2**3 is set if third quadrant is OK 194 | 2**4 is set if fourth quadrant is OK 195 | """ 196 | # we specify order to force contiguous 197 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False, order='C') 198 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False, order='C') 199 | angle_degrees = array( 200 | angle_degrees, ndmin=1, dtype=longdouble, copy=False, order='C' 201 | ) 202 | return super(Mangle, self).check_quadrants( 203 | ra, dec, angle_degrees, 204 | density, max_masked_fraction, 205 | ) 206 | 207 | def calc_simplepix(self, ra, dec): 208 | """ 209 | Calculate simple pixel numbers for list of ra, dec 210 | 211 | parameters 212 | ---------- 213 | ra: scalar or array 214 | Right ascension in degrees. Can be an array. 215 | dec: scalar or array 216 | Declination in degrees. Can be an array. 217 | 218 | output 219 | ------ 220 | Array of zeros or ones 221 | """ 222 | ra = array(ra, ndmin=1, dtype=longdouble, copy=False) 223 | dec = array(dec, ndmin=1, dtype=longdouble, copy=False) 224 | return super(Mangle, self).calc_simplepix(ra, dec) 225 | 226 | def _set_weights(self, weights): 227 | # check length of array... 228 | npoly = _mangle.Mangle.get_npoly(self) 229 | if (weights.size != npoly): 230 | raise IndexError( 231 | "Must set weights for full list of %d polygons." % (npoly) 232 | ) 233 | 234 | # make long doubles 235 | weights = array(weights, ndmin=1, dtype=longdouble, copy=False) 236 | 237 | super(Mangle, self).set_weights(weights) 238 | 239 | filename = property(_mangle.Mangle.get_filename, doc="The mask filename") 240 | weightfile = property( 241 | _mangle.Mangle.get_weightfile, doc="The weight filename (optional)" 242 | ) 243 | area = property(_mangle.Mangle.get_area, doc="The area of the mask") 244 | npoly = property( 245 | _mangle.Mangle.get_npoly, doc="The number of polygons in the mask" 246 | ) 247 | is_pixelized = property( 248 | _mangle.Mangle.get_is_pixelized, doc="True if pixelized." 249 | ) 250 | pixeltype = property( 251 | _mangle.Mangle.get_pixeltype, 252 | doc="The pixelization type, 'u' of unpixelized" 253 | ) 254 | pixelres = property( 255 | _mangle.Mangle.get_pixelres, 256 | doc="The pixel resolution, -1 if unpixelized", 257 | ) 258 | maxpix = property( 259 | _mangle.Mangle.get_pixelres, doc="The maximum pixel value" 260 | ) 261 | is_snapped = property( 262 | _mangle.Mangle.get_is_snapped, doc="True if snapped.", 263 | ) 264 | is_balkanized = property( 265 | _mangle.Mangle.get_is_balkanized, doc="True if balkanized." 266 | ) 267 | areas = property(_mangle.Mangle.get_areas, doc="Area of pixels in mask.") 268 | weights = property( 269 | _mangle.Mangle.get_weights, 270 | _set_weights, doc="Weights of pixels in mask." 271 | ) 272 | 273 | 274 | class Cap(_mangle.Cap): 275 | """ 276 | Class to represent a mangle Cap 277 | 278 | In order to support 128 bit, the input is an array [x,y,z,cm] 279 | """ 280 | def __init__(self, data=None): 281 | """ 282 | Initialize the cap with 128-bit data 283 | 284 | parameters 285 | ---------- 286 | data: array or sequence 287 | An length 4 array of 128 bit floats, or convertable to that. 288 | """ 289 | 290 | if data is not None: 291 | self.set(data) 292 | 293 | def set(self, data): 294 | """ 295 | Initialize the cap with 128-bit data 296 | 297 | parameters 298 | ---------- 299 | data: array or sequence 300 | An length 4 array of 128 bit floats, or convertable to that. 301 | """ 302 | data = array(data, ndmin=1, dtype=longdouble, copy=False) 303 | if data.size != 4: 304 | raise ValueError( 305 | "capdata must be an array of length 4, got %d" % data.size 306 | ) 307 | 308 | super(Cap, self).set(data) 309 | 310 | 311 | class CapVec(_mangle.CapVec): 312 | """ 313 | methods 314 | 315 | cv=CapVec(3) 316 | 317 | len(cv) 318 | cv[index] = data_or_cap 319 | cap = cv[index] 320 | 321 | # getters and setters same as above 322 | cv.set(index,data_or_cap) 323 | cap=cv.get(index) 324 | """ 325 | def set(self, index, data): 326 | """ 327 | Set the cap at the specified element 328 | 329 | parameters 330 | ---------- 331 | index: int 332 | Index into vector 333 | data: sequence or Cap 334 | Data to set the cap in the vector. Can be a sequence/array of 335 | [x,y,z,cm] or a Cap 336 | """ 337 | 338 | sz = self.size() 339 | if index > (sz-1): 340 | raise IndexError("index %s out of bounds: [0,%s)" % (index, sz)) 341 | 342 | if isinstance(data, Cap): 343 | cap = data 344 | else: 345 | cap = Cap(data) 346 | 347 | self._set_cap(index, cap) 348 | 349 | def get(self, index): 350 | """ 351 | get a copy of the Cap specified by the index 352 | 353 | parameters 354 | ---------- 355 | index: int 356 | Index into vector 357 | """ 358 | 359 | sz = self.size() 360 | if index > (sz-1): 361 | raise IndexError("index %s out of bounds: [0,%s)" % (index, sz)) 362 | 363 | return self._get_cap(index) 364 | 365 | def __setitem__(self, index, data): 366 | """ 367 | Set the cap at the specified element 368 | 369 | parameters 370 | ---------- 371 | index: int 372 | Index into vector 373 | data: sequence or Cap 374 | Data to set the cap in the vector. Can be a sequence/array of 375 | [x,y,z,cm] 376 | """ 377 | self.set(index, data) 378 | 379 | def __getitem__(self, index): 380 | """ 381 | get a copy of the Cap specified by the index 382 | 383 | parameters 384 | ---------- 385 | index: int 386 | Index into vector 387 | """ 388 | return self.get(index) 389 | 390 | def __len__(self): 391 | """ 392 | length of CapVec 393 | """ 394 | return self.size() 395 | 396 | 397 | class Polygon(_mangle.Polygon): 398 | """ 399 | class representing a mangle Polygon 400 | 401 | methods 402 | 403 | poly=Polygon(poly_id, pixel_id, weight, cap_vec) 404 | 405 | len(poly) 406 | # copy of a cap 407 | cap = poly[index] 408 | 409 | # getters and setters same as above 410 | cap=cv.get(index) 411 | """ 412 | def __init__(self, poly_id, pixel_id, weight, cap_vec): 413 | if not isinstance(cap_vec, CapVec): 414 | raise ValueError("cap_vec must be of " 415 | "type CapVec, got %s" % type(cap_vec)) 416 | 417 | wtarr = array(weight, ndmin=1, dtype=longdouble, copy=False) 418 | 419 | super(Polygon, self).__init__( 420 | poly_id, 421 | pixel_id, 422 | wtarr, 423 | cap_vec, 424 | ) 425 | 426 | def get(self, index): 427 | """ 428 | get a copy of the Cap specified by the index 429 | 430 | parameters 431 | ---------- 432 | index: int 433 | Index into polygon 434 | """ 435 | 436 | return self.get(index) 437 | 438 | def __getitem__(self, index): 439 | """ 440 | get a copy of the Cap specified by the index 441 | 442 | parameters 443 | ---------- 444 | index: int 445 | Index into polygon 446 | """ 447 | sz = self.size() 448 | if index > (sz-1): 449 | raise IndexError("index %s out of bounds: [0,%s)" % (index, sz)) 450 | 451 | return self._get_cap(index) 452 | 453 | def __len__(self): 454 | """ 455 | length of CapVec 456 | """ 457 | return self.size() 458 | -------------------------------------------------------------------------------- /pymangle/polygon.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "polygon.h" 5 | #include "cap.h" 6 | #include "point.h" 7 | 8 | struct Polygon* polygon_new(void) 9 | { 10 | struct Polygon* self=NULL; 11 | 12 | self=calloc(1, sizeof(struct Polygon)); 13 | if (self==NULL) { 14 | return NULL; 15 | } 16 | 17 | self->poly_id=-9999; 18 | self->pixel_id=-9999; 19 | self->weight=-9999; 20 | self->area=-9999; 21 | 22 | self->caps = capvec_new(); 23 | if (self->caps==NULL) { 24 | free(self); 25 | return NULL; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | struct Polygon* polygon_zeros(size_t n) 32 | { 33 | struct Polygon* self=NULL; 34 | 35 | self=calloc(1, sizeof(struct Polygon)); 36 | if (self==NULL) { 37 | return NULL; 38 | } 39 | 40 | self->poly_id=-9999; 41 | self->pixel_id=-9999; 42 | self->weight=-9999; 43 | self->area=-9999; 44 | 45 | self->caps = capvec_zeros(n); 46 | if (self->caps==NULL) { 47 | free(self); 48 | return NULL; 49 | } 50 | 51 | return self; 52 | } 53 | 54 | 55 | struct Polygon* polygon_free(struct Polygon* self) 56 | { 57 | if (self) { 58 | if (self->caps) { 59 | self->caps=capvec_free(self->caps); 60 | } 61 | free(self); 62 | self=NULL; 63 | } 64 | return self; 65 | } 66 | 67 | struct Polygon* polygon_copy(const struct Polygon* self) 68 | { 69 | struct Polygon* poly=NULL; 70 | 71 | poly=calloc(1, sizeof(struct Polygon)); 72 | if (!poly) { 73 | return NULL; 74 | } 75 | // copy metadata 76 | memcpy(poly, self, sizeof(struct Polygon)); 77 | 78 | // now copy full cap vector 79 | poly->caps = capvec_copy(self->caps); 80 | if (!poly->caps) { 81 | free(poly); 82 | poly=NULL; 83 | return poly; 84 | } 85 | 86 | return poly; 87 | } 88 | 89 | int polygon_reserve(struct Polygon* self, size_t new_capacity) 90 | { 91 | int status=0; 92 | 93 | status=capvec_reserve( self->caps, new_capacity ); 94 | 95 | return status; 96 | } 97 | 98 | int polygon_resize(struct Polygon* self, size_t new_size) 99 | { 100 | int status=0; 101 | 102 | status=capvec_resize( self->caps, new_size ); 103 | // no need to alter area since zeros were added 104 | 105 | return status; 106 | } 107 | 108 | int polygon_clear(struct Polygon* self) 109 | { 110 | int status=0; 111 | 112 | self->poly_id=-9999; 113 | self->pixel_id=-9999; 114 | self->weight=-9999; 115 | self->area=-9999; 116 | self->area_set=0; 117 | 118 | status=capvec_clear( self->caps ); 119 | 120 | return status; 121 | } 122 | 123 | int polygon_push_cap(struct Polygon* self, const struct Cap* cap) 124 | { 125 | int status=0; 126 | 127 | status = capvec_push(self->caps, cap); 128 | return status; 129 | } 130 | 131 | // expanding this code to avoid an extra copy 132 | struct Cap polygon_pop_cap(struct Polygon* self) 133 | { 134 | size_t index=0; 135 | 136 | if (self->caps->size > 0) { 137 | index = self->caps->size-1; 138 | self->caps->size--; 139 | } else { 140 | fprintf(stderr, 141 | "CapVecError: attempt to pop from empty vector, returning garbage\n"); 142 | } 143 | 144 | return self->caps->data[index]; 145 | } 146 | 147 | 148 | int polygon_has_zero_area(const struct Polygon* self) { 149 | size_t i=0; 150 | int has_zero_area=0; 151 | struct Cap* cap=NULL; 152 | 153 | for (i=0; icaps->size; i++) { 154 | cap=&self->caps->data[i]; 155 | if (cap->cm == 0.0L || cap->cm <= -2.0L) { 156 | has_zero_area=1; 157 | break; 158 | } 159 | } 160 | 161 | return has_zero_area; 162 | } 163 | 164 | int is_in_poly(struct Polygon* ply, struct Point* pt) 165 | { 166 | size_t i=0; 167 | struct Cap* cap=NULL; 168 | 169 | int inpoly=1; 170 | 171 | 172 | for (i=0; icaps->size; i++) { 173 | cap = &ply->caps->data[i]; 174 | inpoly = inpoly && is_in_cap(cap, pt); 175 | if (!inpoly) { 176 | break; 177 | } 178 | } 179 | return inpoly; 180 | } 181 | 182 | 183 | /* 184 | long double polygon_calc_area(const struct CapVec* self, long double *tol) 185 | { 186 | long double area=0; 187 | // static work structure 188 | static struct CapVec* dcaps=NULL; 189 | size_t index=0; 190 | long double cm_min=0; 191 | long double darea=0; 192 | 193 | capvec_min_cm(self, &index, &cm_min); 194 | 195 | 196 | return area; 197 | } 198 | */ 199 | 200 | int read_into_polygon(FILE* fptr, struct Polygon* ply) 201 | { 202 | int status=1; 203 | struct Cap* cap=NULL; 204 | 205 | size_t ncaps=0, i=0; 206 | 207 | if (!read_polygon_header(fptr, ply, &ncaps)) { 208 | status=0; 209 | goto _read_single_polygon_errout; 210 | } 211 | 212 | if (ply->caps) { 213 | capvec_resize(ply->caps, ncaps); 214 | } else { 215 | ply->caps = capvec_zeros(ncaps); 216 | if (ply->caps == NULL) { 217 | status=0; 218 | goto _read_single_polygon_errout; 219 | } 220 | } 221 | 222 | for (i=0; icaps->data[i]; 224 | status = read_cap(fptr, cap); 225 | if (status != 1) { 226 | goto _read_single_polygon_errout; 227 | } 228 | } 229 | 230 | _read_single_polygon_errout: 231 | return status; 232 | } 233 | 234 | /* 235 | * parse the polygon "header" for the index poly_index 236 | * 237 | * this is after reading the initial 'polygon' token 238 | */ 239 | 240 | static inline int get_next_nonblank(const char *linebuf, size_t n, size_t i) 241 | { 242 | while (i < n) { 243 | if (linebuf[i] != ' ') { 244 | break; 245 | } 246 | i++; 247 | } 248 | return i; 249 | } 250 | static inline int get_next_blank(const char *linebuf, size_t n, size_t i) 251 | { 252 | while (i < n) { 253 | if (linebuf[i] == ' ') { 254 | break; 255 | } 256 | i++; 257 | } 258 | return i; 259 | } 260 | 261 | // 262 | // this is a horrible mess because the mangle docs are not strict 263 | // about the header 264 | // 265 | 266 | int read_polygon_header(FILE* fptr, struct Polygon* ply, size_t* ncaps) 267 | { 268 | int status=1, read_ncaps=0; 269 | char kwbuff[20]; 270 | char valbuff[25]; 271 | char *linebuf=NULL; 272 | size_t i=0, n=0; 273 | ssize_t nread=0; 274 | 275 | ply->weight=1; 276 | ply->area=0; 277 | ply->pixel_id=-9999; 278 | 279 | nread = getline(&linebuf, &n, fptr); 280 | if (nread == -1) { 281 | status=0; 282 | wlog("Failed to read header line\n"); 283 | goto _read_polygon_header_errout; 284 | } 285 | //fprintf(stderr,"line: %s", linebuf); 286 | 287 | 288 | // find first non-space character 289 | i=0; 290 | i = get_next_nonblank(linebuf, nread, i); 291 | if (linebuf[i] == '\n') { 292 | status=0; 293 | wlog("Failed to read header line\n"); 294 | goto _read_polygon_header_errout; 295 | } 296 | 297 | // we expect to see a number first, which is the polygon id 298 | if (1 != sscanf(&linebuf[i], "%ld", &ply->poly_id)) { 299 | status=0; 300 | wlog("Failed to read polygon id\n"); 301 | goto _read_polygon_header_errout; 302 | } 303 | //fprintf(stderr,"polygon id: %ld\n", ply->poly_id); 304 | 305 | // it is safe to run these in a row without checking 306 | i = get_next_blank(linebuf, nread, i); 307 | i = get_next_nonblank(linebuf, nread, i); 308 | if (linebuf[i] == '\n') { 309 | status=0; 310 | wlog("Failed to find ncaps for polygon id %ld\n", ply->poly_id); 311 | goto _read_polygon_header_errout; 312 | } 313 | 314 | if (linebuf[i] != '(') { 315 | // simple header like this 316 | // polygon 1 4 317 | // just read ncaps now 318 | 319 | if (1 != sscanf(&linebuf[i],"%ld",ncaps)) { 320 | status=0; 321 | wlog("Failed to read ncaps for polygon id %ld\n", ply->poly_id); 322 | goto _read_polygon_header_errout; 323 | } 324 | 325 | } else { 326 | 327 | // complex header like this 328 | // polygon 1 ( 4 caps, 0.666667 weight, 0 pixel, 0.000115712115129 str): 329 | // we do not care about the order, but we do demand the keywords are there 330 | // as well as values so we can tell what is what 331 | // 332 | // the only required entry is the caps 333 | 334 | // skip past the '(' character 335 | i+=1; 336 | 337 | while (1) { 338 | i = get_next_nonblank(linebuf, nread, i); 339 | if (linebuf[i] == '\n' || linebuf[i]=='\0') { 340 | break; 341 | } 342 | if (linebuf[i] == ')') { 343 | break; 344 | } 345 | //fprintf(stderr,"rest of line: '%s'", &linebuf[i]); 346 | 347 | // read the value string 348 | if (1 != sscanf(&linebuf[i],"%s",valbuff)) { 349 | // this should never happen, we already found something that wasn't 350 | // a blank and not end of line 351 | status=0; 352 | wlog("Failed to read header value for polygon %ld\n", ply->poly_id); 353 | goto _read_polygon_header_errout; 354 | } 355 | 356 | i = get_next_blank(linebuf, nread, i); 357 | i = get_next_nonblank(linebuf, nread, i); 358 | if (linebuf[i] == '\n') { 359 | // we didn't find a keyword, the file is misformatted 360 | status=0; 361 | wlog("missing keyword in header for polygon %ld\n", ply->poly_id); 362 | goto _read_polygon_header_errout; 363 | 364 | } 365 | 366 | // read the keyword 367 | if (1 != sscanf(&linebuf[i],"%s",kwbuff)) { 368 | // this should never happen, we already found something that wasn't 369 | // a blank and not end of line 370 | status=0; 371 | 372 | wlog("failed to read keyword in header for polygon %ld\n", ply->poly_id); 373 | goto _read_polygon_header_errout; 374 | } 375 | 376 | if (0==strcmp(kwbuff,"caps,") 377 | || 0==strcmp(kwbuff,"caps") 378 | || 0==strcmp(kwbuff,"caps):") ) { 379 | 380 | if (1 != sscanf(valbuff,"%ld",ncaps)) { 381 | status=0; 382 | wlog("Failed to read ncaps for polygon id %ld", ply->poly_id); 383 | goto _read_polygon_header_errout; 384 | } 385 | read_ncaps=1; 386 | 387 | } else if (0 == strcmp(kwbuff,"weight,") 388 | || 0 == strcmp(kwbuff,"weight") 389 | || 0 == strcmp(kwbuff,"weight):")) { 390 | 391 | if (1 != sscanf(valbuff, "%Lf", &ply->weight)) { 392 | status=0; 393 | wlog("Failed to read pixel for polygon id %ld", ply->poly_id); 394 | goto _read_polygon_header_errout; 395 | } 396 | 397 | } else if (0 == strcmp(kwbuff,"pixel,") 398 | || 0 == strcmp(kwbuff,"pixel") 399 | || 0 == strcmp(kwbuff,"pixel):") ) { 400 | 401 | if (1 != sscanf(valbuff, "%ld", &ply->pixel_id)) { 402 | status=0; 403 | wlog("Failed to read pixel for polygon id %ld", ply->poly_id); 404 | goto _read_polygon_header_errout; 405 | } 406 | 407 | } else if (0 == strcmp(kwbuff,"str):") 408 | || 0 == strcmp(kwbuff,"str,") 409 | || 0 == strcmp(kwbuff,"str")) { 410 | 411 | if (1 != sscanf(valbuff, "%Lf", &ply->area)) { 412 | status=0; 413 | wlog("Failed to read area for polygon id %ld", ply->poly_id); 414 | goto _read_polygon_header_errout; 415 | } 416 | ply->area_set=1; 417 | } 418 | 419 | i = get_next_blank(linebuf, nread, i); 420 | if (i==n || linebuf[i] == '\n' | linebuf[i]=='\0') { 421 | break; 422 | } 423 | 424 | } 425 | 426 | if (!read_ncaps) { 427 | status=0; 428 | wlog("missing caps count for polygon %ld\n", ply->poly_id); 429 | goto _read_polygon_header_errout; 430 | } 431 | } 432 | 433 | _read_polygon_header_errout: 434 | free(linebuf); 435 | return status; 436 | } 437 | 438 | /* 439 | int scan_expected_value(FILE* fptr, char* buff, const char* expected_value) 440 | { 441 | int status=1, res=0; 442 | 443 | res = fscanf(fptr, "%s", buff); 444 | if (1 != res || 0 != strcmp(buff,expected_value)) { 445 | status=0; 446 | wlog("Failed to read expected string in polygon: '%s' " 447 | "got '%s'", expected_value, buff); 448 | } 449 | return status; 450 | } 451 | 452 | 453 | int read_polygon_header(FILE* fptr, struct Polygon* ply, size_t* ncaps) 454 | { 455 | int status=1; 456 | int got_pixel=0; 457 | char kwbuff[20]; 458 | char valbuff[25]; 459 | 460 | if (1 != fscanf(fptr, "%ld", &ply->poly_id)) { 461 | status=0; 462 | wlog("Failed to read polygon id\n"); 463 | goto _read_polygon_header_errout; 464 | } 465 | 466 | if (!scan_expected_value(fptr, kwbuff, "(")) { 467 | status=0; 468 | goto _read_polygon_header_errout; 469 | } 470 | 471 | if (1 != fscanf(fptr,"%ld",ncaps)) { 472 | status=0; 473 | wlog("Failed to read ncaps for polygon id %ld", 474 | ply->poly_id); 475 | goto _read_polygon_header_errout; 476 | } 477 | 478 | 479 | if (!scan_expected_value(fptr, kwbuff, "caps,")) { 480 | status=0; 481 | goto _read_polygon_header_errout; 482 | } 483 | 484 | if (1 != fscanf(fptr,"%Lf",&ply->weight)) { 485 | status=0; 486 | wlog("Failed to read weight for polygon id %ld", 487 | ply->poly_id); 488 | goto _read_polygon_header_errout; 489 | } 490 | 491 | if (!scan_expected_value(fptr, kwbuff, "weight,")) { 492 | status=0; 493 | goto _read_polygon_header_errout; 494 | } 495 | 496 | // pull in the value and keyword 497 | if (2 != fscanf(fptr,"%s %s",valbuff, kwbuff)) { 498 | status=0; 499 | wlog("Failed to read value and keyword (pixel,str) " 500 | "for polygon id %ld\n", 501 | ply->poly_id); 502 | goto _read_polygon_header_errout; 503 | } 504 | 505 | if (0 == strcmp(kwbuff,"pixel,")) { 506 | // we read a pixel value into valbuff 507 | got_pixel=1; 508 | sscanf(valbuff, "%ld", &ply->pixel_id); 509 | } else { 510 | // we probably read the area 511 | if (0 != strcmp(kwbuff,"str):")) { 512 | status=0; 513 | wlog("Expected str): keyword at polygon id %ld, got %s", 514 | ply->poly_id, kwbuff); 515 | goto _read_polygon_header_errout; 516 | } 517 | sscanf(valbuff, "%Lf", &ply->area); 518 | ply->area_set=1; 519 | } 520 | if (got_pixel) { 521 | if (1 != fscanf(fptr,"%Lf",&ply->area)) { 522 | status=0; 523 | wlog("Failed to read area for polygon id %ld", 524 | ply->poly_id); 525 | goto _read_polygon_header_errout; 526 | } 527 | ply->area_set=1; 528 | if (!scan_expected_value(fptr, kwbuff, "str):")) { 529 | status=0; 530 | goto _read_polygon_header_errout; 531 | } 532 | } 533 | 534 | _read_polygon_header_errout: 535 | return status; 536 | } 537 | */ 538 | 539 | void print_polygon(FILE* fptr, struct Polygon* self) 540 | { 541 | if (!self) 542 | return; 543 | 544 | size_t ncaps=0; 545 | struct CapVec* caps=NULL; 546 | caps = self->caps; 547 | ncaps = caps ? caps->size : 0; 548 | 549 | fprintf(fptr, 550 | "polygon %ld ( %ld caps, %.18Lg weight, %ld pixel, %.18Lg str):\n", 551 | self->poly_id, ncaps, self->weight, 552 | self->pixel_id, self->area); 553 | if (ncaps > 0) { 554 | size_t i=0; 555 | for (i=0; idata[i]; 557 | print_cap(fptr,cap); 558 | } 559 | } 560 | 561 | } 562 | 563 | 564 | struct PolyVec* polyvec_new(size_t n) 565 | { 566 | struct PolyVec* self=NULL; 567 | 568 | self=calloc(1, sizeof(struct PolyVec)); 569 | if (self == NULL) { 570 | return NULL; 571 | } 572 | // pointers will be NULL (0) 573 | self->data = calloc(n, sizeof(struct Polygon)); 574 | if (self->data == NULL) { 575 | free(self); 576 | return NULL; 577 | } 578 | 579 | self->size = n; 580 | return self; 581 | } 582 | 583 | struct PolyVec* polyvec_free(struct PolyVec* self) 584 | { 585 | struct Polygon* ply=NULL; 586 | size_t i=0; 587 | if (self != NULL) { 588 | if (self->data!= NULL) { 589 | 590 | for (i=0; isize; i++) { 591 | ply=&self->data[i]; 592 | ply->caps = capvec_free(ply->caps); 593 | } 594 | free(self->data); 595 | 596 | } 597 | free(self); 598 | self=NULL; 599 | } 600 | return self; 601 | } 602 | 603 | struct PolyVec *read_polygons(FILE* fptr, size_t npoly) 604 | { 605 | int status=1; 606 | char buff[_MANGLE_SMALL_BUFFSIZE]; 607 | struct PolyVec *self=NULL; 608 | size_t i=0; 609 | 610 | self = polyvec_new(npoly); 611 | if (!self) { 612 | status=0; 613 | wlog("could not allocate %lu polygons\n", npoly); 614 | goto _read_polygons_bail; 615 | } 616 | 617 | // in order to get here, we had to read the token already 618 | strcpy(buff, "polygon"); 619 | 620 | for (i=0; idata[i]); 630 | if (!status) { 631 | wlog("failed to read polygon %lu\n", i); 632 | break; 633 | } 634 | 635 | if (i != (npoly-1)) { 636 | if (1 != fscanf(fptr,"%s",buff)) { 637 | status=0; 638 | wlog("Error reading token for polygon %lu\n", i); 639 | goto _read_polygons_bail; 640 | } 641 | } 642 | } 643 | 644 | _read_polygons_bail: 645 | 646 | if (!status) { 647 | free(self); 648 | self=NULL; 649 | } 650 | return self; 651 | } 652 | 653 | 654 | 655 | void print_polygons(FILE* fptr, struct PolyVec *self) 656 | { 657 | if (self) { 658 | size_t i; 659 | struct Polygon* ply=NULL; 660 | 661 | for (i=0; isize; i++) { 662 | ply = &self->data[i]; 663 | print_polygon(fptr,ply); 664 | } 665 | } 666 | } 667 | -------------------------------------------------------------------------------- /pymangle/_mangle.c: -------------------------------------------------------------------------------- 1 | #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION 2 | 3 | #include 4 | #include 5 | #include 6 | #include "numpy/arrayobject.h" 7 | #include "mangle.h" 8 | #include "point.h" 9 | #include "cap.h" 10 | #include "polygon.h" 11 | #include "stack.h" 12 | #include "pixel.h" 13 | #include "rand.h" 14 | 15 | /* 16 | Cap class 17 | */ 18 | struct PyMangleCap { 19 | PyObject_HEAD 20 | 21 | struct Cap cap; 22 | }; 23 | 24 | /* 25 | 26 | Initalize the mangle cap. 27 | 28 | In order to support long double, the input is a numpy array of length 4 29 | and type numpy.longdouble. We cannot pass scalars because PyArg_ParseTuple 30 | only supports double 31 | 32 | we are assuming this longdouble corresponds to c long double 33 | 34 | No error checking is performed here, do that in python 35 | */ 36 | 37 | static int 38 | PyMangleCap_init(struct PyMangleCap* self, PyObject *args, PyObject *kwds) 39 | { 40 | return 0; 41 | } 42 | 43 | static PyObject* 44 | PyMangleCap_set(struct PyMangleCap* self, PyObject *args, PyObject *kwds) 45 | { 46 | PyObject* arr_obj=NULL; 47 | long double *data=NULL; 48 | 49 | if (!PyArg_ParseTuple(args, (char*)"O", &arr_obj)) { 50 | return NULL; 51 | } 52 | 53 | data = (long double *) PyArray_DATA( (PyArrayObject*) arr_obj); 54 | 55 | cap_set(&self->cap, data[0], data[1], data[2], data[3]); 56 | 57 | Py_RETURN_NONE; 58 | } 59 | 60 | 61 | static void 62 | PyMangleCap_dealloc(struct PyMangleCap* self) 63 | { 64 | 65 | #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6) || (PY_MAJOR_VERSION == 3)) 66 | Py_TYPE(self)->tp_free((PyObject*)self); 67 | #else 68 | // old way, removed in python 3 69 | self->ob_type->tp_free((PyObject*)self); 70 | #endif 71 | 72 | } 73 | 74 | 75 | static PyObject * 76 | PyMangleCap_repr(struct PyMangleCap* self) { 77 | char buff[128]; 78 | struct Cap* cap=&self->cap; 79 | 80 | snprint_cap(cap, buff, sizeof(buff)); 81 | 82 | #if PY_MAJOR_VERSION >= 3 83 | return PyUnicode_FromString((const char*)buff); 84 | #else 85 | return PyString_FromString((const char*)buff); 86 | #endif 87 | } 88 | 89 | // methods for PyMangleCap 90 | static PyMethodDef PyMangleCap_methods[] = { 91 | {"set", (PyCFunction)PyMangleCap_set, METH_VARARGS, "set the cap data\n"}, 92 | {NULL} 93 | }; 94 | 95 | // the type definition for PyMangleCap 96 | 97 | static PyTypeObject PyMangleCapType = { 98 | #if PY_MAJOR_VERSION >= 3 99 | PyVarObject_HEAD_INIT(NULL, 0) 100 | #else 101 | PyObject_HEAD_INIT(NULL) 102 | 0, /*ob_size*/ 103 | #endif 104 | "_mangle.Mangle", /*tp_name*/ 105 | sizeof(struct PyMangleCap), /*tp_basicsize*/ 106 | 0, /*tp_itemsize*/ 107 | (destructor)PyMangleCap_dealloc, /*tp_dealloc*/ 108 | 0, /*tp_print*/ 109 | 0, /*tp_getattr*/ 110 | 0, /*tp_setattr*/ 111 | 0, /*tp_compare*/ 112 | //0, /*tp_repr*/ 113 | (reprfunc)PyMangleCap_repr, /*tp_repr*/ 114 | 0, /*tp_as_number*/ 115 | 0, /*tp_as_sequence*/ 116 | 0, /*tp_as_mapping*/ 117 | 0, /*tp_hash */ 118 | 0, /*tp_call*/ 119 | 0, /*tp_str*/ 120 | 0, /*tp_getattro*/ 121 | 0, /*tp_setattro*/ 122 | 0, /*tp_as_buffer*/ 123 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 124 | "A class to work with Mangle masks.\n" 125 | "\n" 126 | "construction\n" 127 | " import pymangle\n" 128 | " m=pymangle.Mangle(mask_file, verbose=False)\n" 129 | "\n" 130 | "\n" 131 | "read-only properties\n" 132 | "--------------------\n" 133 | " filename\n" 134 | " area\n" 135 | " npoly\n" 136 | " is_pixelized\n" 137 | " pixeltype\n" 138 | " pixelres\n" 139 | " maxpix\n" 140 | " is_snapped\n" 141 | " is_balkanized\n" 142 | " weightfile\n" 143 | "\n" 144 | "See docs for each property for more details.\n" 145 | "\n" 146 | "methods\n" 147 | "----------------------\n" 148 | " polyid(ra,dec)\n" 149 | " weight(ra,dec)\n" 150 | " polyid_and_weight(ra,dec)\n" 151 | " contains(ra,dec)\n" 152 | " genrand(nrand)\n" 153 | " genrand_range(nrand,ramin,ramax,decmin,decmax)\n" 154 | " calc_simplepix(ra,dec)\n" 155 | " read_weights(weightfile)\n" 156 | "\n" 157 | "getters (correspond to properties above)\n" 158 | "----------------------------------------\n" 159 | " get_filename()\n" 160 | " get_weightfile()\n" 161 | " get_area()\n" 162 | " get_npoly()\n" 163 | " get_is_pixelized()\n" 164 | " get_pixeltype()\n" 165 | " get_pixelres()\n" 166 | " get_maxpix()\n" 167 | " get_is_snapped()\n" 168 | " get_is_balkanized()\n" 169 | " get_pixels()\n" 170 | " get_weights()\n" 171 | " get_areas()\n" 172 | "\n" 173 | "setter (corresponding to property above)\n" 174 | "----------------------------------------\n" 175 | " set_weights(weights)\n" 176 | "\n" 177 | "See docs for each method for more detail.\n", 178 | 0, /* tp_traverse */ 179 | 0, /* tp_clear */ 180 | 0, /* tp_richcompare */ 181 | 0, /* tp_weaklistoffset */ 182 | 0, /* tp_iter */ 183 | 0, /* tp_iternext */ 184 | PyMangleCap_methods, /* tp_methods */ 185 | 0, /* tp_members */ 186 | 0, /* tp_getset */ 187 | 0, /* tp_base */ 188 | 0, /* tp_dict */ 189 | 0, /* tp_descr_get */ 190 | 0, /* tp_descr_set */ 191 | 0, /* tp_dictoffset */ 192 | //0, /* tp_init */ 193 | (initproc)PyMangleCap_init, /* tp_init */ 194 | 0, /* tp_alloc */ 195 | PyType_GenericNew, /* tp_new */ 196 | }; 197 | 198 | 199 | 200 | /* 201 | 202 | CapVec class 203 | 204 | */ 205 | 206 | struct PyMangleCapVec { 207 | PyObject_HEAD 208 | 209 | struct CapVec* caps; 210 | }; 211 | 212 | static int 213 | PyMangleCapVec_init(struct PyMangleCapVec* self, PyObject *args, PyObject *kwds) 214 | { 215 | Py_ssize_t n=0; 216 | 217 | if (!PyArg_ParseTuple(args, (char*)"n", &n)) { 218 | return -1; 219 | } 220 | 221 | self->caps = capvec_zeros( (size_t) n); 222 | if (self->caps == NULL) { 223 | PyErr_SetString(PyExc_MemoryError, "out of memory allocating CapVec"); 224 | return -1; 225 | } 226 | 227 | return 0; 228 | } 229 | 230 | static PyObject * 231 | PyMangleCapVec_repr(struct PyMangleCapVec* self) { 232 | char buff[64]; 233 | 234 | snprintf(buff,64, "MangleCapVec, ncaps: %lu", self->caps->size); 235 | 236 | #if PY_MAJOR_VERSION >= 3 237 | return PyUnicode_FromString((const char*)buff); 238 | #else 239 | return PyString_FromString((const char*)buff); 240 | #endif 241 | } 242 | 243 | 244 | static void 245 | PyMangleCapVec_dealloc(struct PyMangleCapVec* self) 246 | { 247 | 248 | self->caps=capvec_free(self->caps); 249 | 250 | #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6) || (PY_MAJOR_VERSION == 3)) 251 | Py_TYPE(self)->tp_free((PyObject*)self); 252 | #else 253 | // old way, removed in python 3 254 | self->ob_type->tp_free((PyObject*)self); 255 | #endif 256 | } 257 | 258 | static PyObject* 259 | PyMangleCapVec_size(struct PyMangleCapVec* self) 260 | { 261 | return Py_BuildValue("n",(Py_ssize_t) self->caps->size); 262 | } 263 | 264 | 265 | 266 | static PyObject* 267 | PyMangleCapVec_set_cap(struct PyMangleCapVec* self, PyObject *args, PyObject *kwds) 268 | { 269 | 270 | PyObject* cap_pyobj=NULL; 271 | struct PyMangleCap *cap_obj=NULL; 272 | 273 | Py_ssize_t index=0; 274 | 275 | if (!PyArg_ParseTuple(args, (char*)"nO", &index, &cap_pyobj)) { 276 | return NULL; 277 | } 278 | 279 | cap_obj = (struct PyMangleCap *) cap_pyobj; 280 | 281 | cap_set(&self->caps->data[index], 282 | cap_obj->cap.x, 283 | cap_obj->cap.y, 284 | cap_obj->cap.z, 285 | cap_obj->cap.cm); 286 | 287 | Py_RETURN_NONE; 288 | } 289 | 290 | static PyObject* 291 | PyMangleCapVec_get_cap(struct PyMangleCapVec* self, PyObject *args, PyObject *kwds) 292 | { 293 | PyTypeObject *type=&PyMangleCapType; 294 | 295 | const struct Cap* cap=NULL; 296 | PyObject* cap_copy=NULL; 297 | struct PyMangleCap* cap_st=NULL; 298 | 299 | Py_ssize_t index=0; 300 | 301 | if (!PyArg_ParseTuple(args, (char*)"n", &index)) { 302 | return NULL; 303 | } 304 | 305 | 306 | cap = &self->caps->data[index]; 307 | 308 | cap_copy = _PyObject_New(type); 309 | cap_st=(struct PyMangleCap *) cap_copy; 310 | 311 | cap_st->cap.x = cap->x; 312 | cap_st->cap.y = cap->y; 313 | cap_st->cap.z = cap->z; 314 | cap_st->cap.cm = cap->cm; 315 | 316 | return cap_copy; 317 | } 318 | 319 | 320 | 321 | 322 | 323 | // methods for PyMangleCapVec 324 | static PyMethodDef PyMangleCapVec_methods[] = { 325 | {"size", (PyCFunction)PyMangleCapVec_size, METH_VARARGS, "get length of CapVec\n"}, 326 | {"_set_cap", (PyCFunction)PyMangleCapVec_set_cap, METH_VARARGS, "set the cap data at the specified index\n"}, 327 | {"_get_cap", (PyCFunction)PyMangleCapVec_get_cap, METH_VARARGS, "get a copy of the cap at the specified index\n"}, 328 | {NULL} 329 | }; 330 | 331 | // the type definition for PyMangleCapVec 332 | 333 | static PyTypeObject PyMangleCapVecType = { 334 | #if PY_MAJOR_VERSION >= 3 335 | PyVarObject_HEAD_INIT(NULL, 0) 336 | #else 337 | PyObject_HEAD_INIT(NULL) 338 | 0, /*ob_size*/ 339 | #endif 340 | "_mangle.Mangle", /*tp_name*/ 341 | sizeof(struct PyMangleCapVec), /*tp_basicsize*/ 342 | 0, /*tp_itemsize*/ 343 | (destructor)PyMangleCapVec_dealloc, /*tp_dealloc*/ 344 | 0, /*tp_print*/ 345 | 0, /*tp_getattr*/ 346 | 0, /*tp_setattr*/ 347 | 0, /*tp_compare*/ 348 | //0, /*tp_repr*/ 349 | (reprfunc)PyMangleCapVec_repr, /*tp_repr*/ 350 | 0, /*tp_as_number*/ 351 | 0, /*tp_as_sequence*/ 352 | 0, /*tp_as_mapping*/ 353 | 0, /*tp_hash */ 354 | 0, /*tp_call*/ 355 | 0, /*tp_str*/ 356 | 0, /*tp_getattro*/ 357 | 0, /*tp_setattro*/ 358 | 0, /*tp_as_buffer*/ 359 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 360 | "A class to work with Mangle masks.\n" 361 | "\n" 362 | "construction\n" 363 | " import pymangle\n" 364 | " m=pymangle.Mangle(mask_file, verbose=False)\n" 365 | "\n" 366 | "\n" 367 | "read-only properties\n" 368 | "--------------------\n" 369 | " filename\n" 370 | " area\n" 371 | " npoly\n" 372 | " is_pixelized\n" 373 | " pixeltype\n" 374 | " pixelres\n" 375 | " maxpix\n" 376 | " is_snapped\n" 377 | " is_balkanized\n" 378 | " weightfile\n" 379 | "\n" 380 | "See docs for each property for more details.\n" 381 | "\n" 382 | "methods\n" 383 | "----------------------\n" 384 | " polyid(ra,dec)\n" 385 | " weight(ra,dec)\n" 386 | " polyid_and_weight(ra,dec)\n" 387 | " contains(ra,dec)\n" 388 | " genrand(nrand)\n" 389 | " genrand_range(nrand,ramin,ramax,decmin,decmax)\n" 390 | " calc_simplepix(ra,dec)\n" 391 | " read_weights(weightfile)\n" 392 | "\n" 393 | "getters (correspond to properties above)\n" 394 | "----------------------------------------\n" 395 | " get_filename()\n" 396 | " get_weightfile()\n" 397 | " get_area()\n" 398 | " get_npoly()\n" 399 | " get_is_pixelized()\n" 400 | " get_pixeltype()\n" 401 | " get_pixelres()\n" 402 | " get_maxpix()\n" 403 | " get_is_snapped()\n" 404 | " get_is_balkanized()\n" 405 | " get_pixels()\n" 406 | " get_weights()\n" 407 | " get_areas()\n" 408 | "\n" 409 | "setter (corresponding to property above)\n" 410 | "----------------------------------------\n" 411 | " set_weights(weights)\n" 412 | "\n" 413 | "See docs for each method for more detail.\n", 414 | 0, /* tp_traverse */ 415 | 0, /* tp_clear */ 416 | 0, /* tp_richcompare */ 417 | 0, /* tp_weaklistoffset */ 418 | 0, /* tp_iter */ 419 | 0, /* tp_iternext */ 420 | PyMangleCapVec_methods, /* tp_methods */ 421 | 0, /* tp_members */ 422 | 0, /* tp_getset */ 423 | 0, /* tp_base */ 424 | 0, /* tp_dict */ 425 | 0, /* tp_descr_get */ 426 | 0, /* tp_descr_set */ 427 | 0, /* tp_dictoffset */ 428 | //0, /* tp_init */ 429 | (initproc)PyMangleCapVec_init, /* tp_init */ 430 | 0, /* tp_alloc */ 431 | PyType_GenericNew, /* tp_new */ 432 | }; 433 | 434 | 435 | 436 | /* 437 | Polygon class 438 | */ 439 | 440 | struct PyManglePolygon { 441 | PyObject_HEAD 442 | 443 | struct Polygon poly; 444 | }; 445 | 446 | /* 447 | Initialize from a CapVec 448 | 449 | The data CapVec are copied 450 | */ 451 | 452 | static int 453 | PyManglePolygon_init(struct PyManglePolygon* self, PyObject *args, PyObject *kwds) 454 | { 455 | int64 poly_id=0, pixel_id=0; 456 | PyObject* wt_arr_obj=NULL; 457 | long double *wt_data=NULL; 458 | 459 | PyObject *caps_obj=NULL; 460 | struct PyMangleCapVec *caps_st=NULL; 461 | 462 | if (!PyArg_ParseTuple(args, (char*)"llOO", &poly_id, &pixel_id, &wt_arr_obj, &caps_obj)) { 463 | return -1; 464 | } 465 | 466 | wt_data = (long double *) PyArray_DATA( (PyArrayObject*) wt_arr_obj); 467 | 468 | caps_st=(struct PyMangleCapVec *) caps_obj; 469 | 470 | self->poly.poly_id = poly_id; 471 | self->poly.pixel_id = pixel_id; 472 | self->poly.weight = wt_data[0]; 473 | 474 | // we need garea to calculate this 475 | self->poly.area = -1.0; 476 | 477 | self->poly.caps = capvec_copy(caps_st->caps); 478 | if (self->poly.caps == NULL) { 479 | PyErr_SetString(PyExc_MemoryError, "out of memory allocating CapVec"); 480 | return -1; 481 | } 482 | 483 | return 0; 484 | } 485 | 486 | static void 487 | PyManglePolygon_dealloc(struct PyManglePolygon* self) 488 | { 489 | 490 | self->poly.caps=capvec_free(self->poly.caps); 491 | 492 | #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6) || (PY_MAJOR_VERSION == 3)) 493 | Py_TYPE(self)->tp_free((PyObject*)self); 494 | #else 495 | // old way, removed in python 3 496 | self->ob_type->tp_free((PyObject*)self); 497 | #endif 498 | } 499 | 500 | static PyObject * 501 | PyManglePolygon_repr(struct PyManglePolygon* self) { 502 | char buff[64]; 503 | 504 | snprintf(buff, 505 | sizeof(buff), 506 | "poly_id: %ld pixel_id: %ld weight: %.18Lg area: %.18Lg ncaps: %lu", 507 | self->poly.poly_id, 508 | self->poly.pixel_id, 509 | self->poly.weight, 510 | self->poly.area, 511 | self->poly.caps->size); 512 | 513 | #if PY_MAJOR_VERSION >= 3 514 | return PyUnicode_FromString((const char*)buff); 515 | #else 516 | return PyString_FromString((const char*)buff); 517 | #endif 518 | } 519 | 520 | 521 | static PyObject* 522 | PyManglePolygon_size(struct PyManglePolygon* self) 523 | { 524 | return Py_BuildValue("n",(Py_ssize_t) self->poly.caps->size); 525 | } 526 | 527 | static PyObject* 528 | PyManglePolygon_get_cap(struct PyManglePolygon* self, PyObject *args, PyObject *kwds) 529 | { 530 | PyTypeObject *type=&PyMangleCapType; 531 | 532 | const struct Cap* cap=NULL; 533 | PyObject* cap_copy=NULL; 534 | struct PyMangleCap* cap_st=NULL; 535 | 536 | Py_ssize_t index=0; 537 | 538 | if (!PyArg_ParseTuple(args, (char*)"n", &index)) { 539 | return NULL; 540 | } 541 | 542 | 543 | cap = &self->poly.caps->data[index]; 544 | 545 | cap_copy = _PyObject_New(type); 546 | cap_st=(struct PyMangleCap *) cap_copy; 547 | 548 | cap_st->cap.x = cap->x; 549 | cap_st->cap.y = cap->y; 550 | cap_st->cap.z = cap->z; 551 | cap_st->cap.cm = cap->cm; 552 | 553 | return cap_copy; 554 | } 555 | 556 | 557 | 558 | 559 | // methods for PyManglePolygon 560 | static PyMethodDef PyManglePolygon_methods[] = { 561 | {"size", (PyCFunction)PyManglePolygon_size, METH_VARARGS, "get length of Polygon\n"}, 562 | {"_get_cap", (PyCFunction)PyManglePolygon_get_cap, METH_VARARGS, "get a copy of the cap at the specified index\n"}, 563 | {NULL} 564 | }; 565 | 566 | // the type definition for PyManglePolygon 567 | 568 | static PyTypeObject PyManglePolygonType = { 569 | #if PY_MAJOR_VERSION >= 3 570 | PyVarObject_HEAD_INIT(NULL, 0) 571 | #else 572 | PyObject_HEAD_INIT(NULL) 573 | 0, /*ob_size*/ 574 | #endif 575 | "_mangle.Mangle", /*tp_name*/ 576 | sizeof(struct PyManglePolygon), /*tp_basicsize*/ 577 | 0, /*tp_itemsize*/ 578 | (destructor)PyManglePolygon_dealloc, /*tp_dealloc*/ 579 | 0, /*tp_print*/ 580 | 0, /*tp_getattr*/ 581 | 0, /*tp_setattr*/ 582 | 0, /*tp_compare*/ 583 | //0, /*tp_repr*/ 584 | (reprfunc)PyManglePolygon_repr, /*tp_repr*/ 585 | 0, /*tp_as_number*/ 586 | 0, /*tp_as_sequence*/ 587 | 0, /*tp_as_mapping*/ 588 | 0, /*tp_hash */ 589 | 0, /*tp_call*/ 590 | 0, /*tp_str*/ 591 | 0, /*tp_getattro*/ 592 | 0, /*tp_setattro*/ 593 | 0, /*tp_as_buffer*/ 594 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 595 | "A class to work with Mangle masks.\n" 596 | "\n" 597 | "construction\n" 598 | " import pymangle\n" 599 | " m=pymangle.Mangle(mask_file, verbose=False)\n" 600 | "\n" 601 | "\n" 602 | "read-only properties\n" 603 | "--------------------\n" 604 | " filename\n" 605 | " area\n" 606 | " npoly\n" 607 | " is_pixelized\n" 608 | " pixeltype\n" 609 | " pixelres\n" 610 | " maxpix\n" 611 | " is_snapped\n" 612 | " is_balkanized\n" 613 | " weightfile\n" 614 | "\n" 615 | "See docs for each property for more details.\n" 616 | "\n" 617 | "methods\n" 618 | "----------------------\n" 619 | " polyid(ra,dec)\n" 620 | " weight(ra,dec)\n" 621 | " polyid_and_weight(ra,dec)\n" 622 | " contains(ra,dec)\n" 623 | " genrand(nrand)\n" 624 | " genrand_range(nrand,ramin,ramax,decmin,decmax)\n" 625 | " calc_simplepix(ra,dec)\n" 626 | " read_weights(weightfile)\n" 627 | "\n" 628 | "getters (correspond to properties above)\n" 629 | "----------------------------------------\n" 630 | " get_filename()\n" 631 | " get_weightfile()\n" 632 | " get_area()\n" 633 | " get_npoly()\n" 634 | " get_is_pixelized()\n" 635 | " get_pixeltype()\n" 636 | " get_pixelres()\n" 637 | " get_maxpix()\n" 638 | " get_is_snapped()\n" 639 | " get_is_balkanized()\n" 640 | " get_pixels()\n" 641 | " get_weights()\n" 642 | " get_areas()\n" 643 | "\n" 644 | "setter (corresponding to property above)\n" 645 | "----------------------------------------\n" 646 | " set_weights(weights)\n" 647 | "\n" 648 | "See docs for each method for more detail.\n", 649 | 0, /* tp_traverse */ 650 | 0, /* tp_clear */ 651 | 0, /* tp_richcompare */ 652 | 0, /* tp_weaklistoffset */ 653 | 0, /* tp_iter */ 654 | 0, /* tp_iternext */ 655 | PyManglePolygon_methods, /* tp_methods */ 656 | 0, /* tp_members */ 657 | 0, /* tp_getset */ 658 | 0, /* tp_base */ 659 | 0, /* tp_dict */ 660 | 0, /* tp_descr_get */ 661 | 0, /* tp_descr_set */ 662 | 0, /* tp_dictoffset */ 663 | //0, /* tp_init */ 664 | (initproc)PyManglePolygon_init, /* tp_init */ 665 | 0, /* tp_alloc */ 666 | PyType_GenericNew, /* tp_new */ 667 | }; 668 | 669 | 670 | 671 | 672 | /* 673 | 674 | overall MangleMask class 675 | 676 | */ 677 | 678 | 679 | struct PyMangleMask { 680 | PyObject_HEAD 681 | 682 | struct MangleMask* mask; 683 | }; 684 | 685 | 686 | 687 | /* 688 | * Initalize the mangle mask. Read the file and, if pixelized, 689 | * set the pixel mask 690 | */ 691 | 692 | static int 693 | PyMangleMask_init(struct PyMangleMask* self, PyObject *args, PyObject *kwds) 694 | { 695 | char* filename=NULL; 696 | int verbose=0; 697 | if (!PyArg_ParseTuple(args, (char*)"si", &filename, &verbose)) { 698 | return -1; 699 | } 700 | 701 | self->mask = mangle_new(); 702 | if (!self->mask) { 703 | PyErr_SetString(PyExc_MemoryError, "Error creating mangle mask struct"); 704 | return -1; 705 | } 706 | mangle_set_verbosity(self->mask, verbose); 707 | if (!mangle_read(self->mask, filename)) { 708 | PyErr_Format(PyExc_IOError, "Error reading mangle mask %s",filename); 709 | return -1; 710 | } 711 | return 0; 712 | } 713 | 714 | static PyObject * 715 | PyMangleMask_read_weights(struct PyMangleMask* self, PyObject *args, PyObject *kwds) 716 | { 717 | char *weightfile = NULL; 718 | 719 | if (!PyArg_ParseTuple(args, (char*)"s", &weightfile)) { 720 | Py_RETURN_FALSE; 721 | } 722 | 723 | if (!mangle_read_weights(self->mask, weightfile)) { 724 | PyErr_Format(PyExc_IOError,"Error reading weight file %s",weightfile); 725 | Py_RETURN_FALSE; 726 | } 727 | 728 | Py_RETURN_TRUE; 729 | } 730 | 731 | static PyObject * 732 | PyMangleMask_set_weights(struct PyMangleMask* self, PyObject *args, PyObject *kwds) 733 | { 734 | PyObject *weight_obj = NULL; 735 | long double *weights; 736 | 737 | if (!PyArg_ParseTuple(args, (char*)"O", &weight_obj)) { 738 | PyErr_SetString(PyExc_TypeError,"Failed to parse args to set_weights"); 739 | Py_RETURN_FALSE; 740 | } 741 | 742 | if (PyArray_NDIM((PyArrayObject *)weight_obj) != 1) { 743 | PyErr_SetString(PyExc_ValueError,"Input to set_weights must be 1D array"); 744 | Py_RETURN_FALSE; 745 | } 746 | 747 | if (PyArray_DIM((PyArrayObject *)weight_obj, 0) != self->mask->npoly) { 748 | PyErr_SetString(PyExc_ValueError,"Input number of weights must be equal to number of polygons."); 749 | Py_RETURN_FALSE; 750 | } 751 | 752 | weights = (long double *) PyArray_DATA((PyArrayObject *)weight_obj); 753 | 754 | if (!mangle_set_weights(self->mask, weights)) { 755 | PyErr_SetString(PyExc_ValueError,"Error setting weights"); 756 | Py_RETURN_FALSE; 757 | } 758 | 759 | Py_RETURN_TRUE; 760 | } 761 | 762 | /* 763 | * we use sprintf since PyString_FromFormat doesn't accept floating point types 764 | */ 765 | 766 | static PyObject * 767 | PyMangleMask_repr(struct PyMangleMask* self) { 768 | npy_intp npoly; 769 | npy_intp npix; 770 | char buff[4096]; 771 | struct MangleMask* mask=NULL; 772 | 773 | mask = self->mask; 774 | npoly = (mask->poly_vec != NULL) ? mask->poly_vec->size : 0; 775 | npix = (mask->pixel_list_vec != NULL) ? mask->pixel_list_vec->size : 0; 776 | 777 | snprintf(buff,4096, 778 | "Mangle\n" 779 | "\tfile: %s\n" 780 | "\tarea: %Lg sqdeg\n" 781 | "\tnpoly: %ld\n" 782 | "\tpixeltype: '%c'\n" 783 | "\tpixelres: %ld\n" 784 | "\treal: %d\n" 785 | "\tnpix: %ld\n" 786 | "\tsnapped: %d\n" 787 | "\tbalkanized: %d\n" 788 | "\tweightfile: %s\n" 789 | "\tverbose: %d\n", 790 | mask->filename, mask->total_area*R2D*R2D, 791 | npoly, mask->pixeltype, mask->pixelres, mask->real, npix, 792 | mask->snapped, mask->balkanized, mask->weightfile, 793 | mask->verbose); 794 | #if PY_MAJOR_VERSION >= 3 795 | return PyUnicode_FromString((const char*)buff); 796 | #else 797 | return PyString_FromString((const char*)buff); 798 | #endif 799 | } 800 | 801 | static PyObject * 802 | PyMangleMask_area(struct PyMangleMask* self) { 803 | PyObject *longdouble_obj = NULL; 804 | npy_intp dims[1] = {1}; 805 | long double total_area_deg2; 806 | 807 | longdouble_obj = PyArray_ZEROS(0,dims,NPY_LONGDOUBLE,0); 808 | total_area_deg2 = self->mask->total_area*R2D*R2D; 809 | memcpy(PyArray_DATA((PyArrayObject *)longdouble_obj), &total_area_deg2, sizeof(long double)); 810 | return PyArray_Return((PyArrayObject *)longdouble_obj); 811 | } 812 | static PyObject * 813 | PyMangleMask_npoly(struct PyMangleMask* self) { 814 | return PyLong_FromLongLong( (PY_LONG_LONG) self->mask->npoly); 815 | } 816 | 817 | 818 | static PyObject * 819 | PyMangleMask_is_pixelized(struct PyMangleMask* self) { 820 | if (self->mask->pixeltype != 'u') { 821 | Py_RETURN_TRUE; 822 | } else { 823 | Py_RETURN_FALSE; 824 | } 825 | } 826 | static PyObject * 827 | PyMangleMask_pixeltype(struct PyMangleMask* self) { 828 | char ptype[2]; 829 | ptype[0] = self->mask->pixeltype; 830 | ptype[1] = '\0'; 831 | #if PY_MAJOR_VERSION >= 3 832 | return PyUnicode_FromString((const char*) ptype); 833 | #else 834 | return PyString_FromString((const char* ) ptype); 835 | #endif 836 | } 837 | static PyObject * 838 | PyMangleMask_pixelres(struct PyMangleMask* self) { 839 | return PyLong_FromLongLong( (PY_LONG_LONG) self->mask->pixelres); 840 | } 841 | static PyObject * 842 | PyMangleMask_maxpix(struct PyMangleMask* self) { 843 | return PyLong_FromLongLong( (PY_LONG_LONG) self->mask->maxpix); 844 | } 845 | 846 | static PyObject * 847 | PyMangleMask_filename(struct PyMangleMask* self) { 848 | #if PY_MAJOR_VERSION >= 3 849 | return PyUnicode_FromString( (const char* ) self->mask->filename); 850 | #else 851 | return PyString_FromString( (const char* ) self->mask->filename); 852 | #endif 853 | } 854 | 855 | static PyObject * 856 | PyMangleMask_weightfile(struct PyMangleMask* self) { 857 | 858 | 859 | #if PY_MAJOR_VERSION >= 3 860 | return PyUnicode_FromString( (const char* ) self->mask->weightfile); 861 | #else 862 | return PyString_FromString( (const char* ) self->mask->weightfile); 863 | #endif 864 | } 865 | 866 | 867 | 868 | 869 | 870 | static PyObject * 871 | PyMangleMask_is_snapped(struct PyMangleMask* self) { 872 | if (self->mask->snapped) { 873 | Py_RETURN_TRUE; 874 | } else { 875 | Py_RETURN_FALSE; 876 | } 877 | } 878 | static PyObject * 879 | PyMangleMask_is_balkanized(struct PyMangleMask* self) { 880 | if (self->mask->balkanized) { 881 | Py_RETURN_TRUE; 882 | } else { 883 | Py_RETURN_FALSE; 884 | } 885 | } 886 | 887 | 888 | static void 889 | cleanup(struct PyMangleMask* self) 890 | { 891 | if (self->mask->verbose > 2) 892 | fprintf(stderr,"mask struct\n"); 893 | self->mask = mangle_free(self->mask); 894 | } 895 | 896 | 897 | 898 | static void 899 | PyMangleMask_dealloc(struct PyMangleMask* self) 900 | { 901 | 902 | cleanup(self); 903 | 904 | #if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6) || (PY_MAJOR_VERSION == 3)) 905 | Py_TYPE(self)->tp_free((PyObject*)self); 906 | #else 907 | // old way, removed in python 3 908 | self->ob_type->tp_free((PyObject*)self); 909 | #endif 910 | } 911 | 912 | 913 | static PyObject* 914 | make_bool_array(npy_intp size, const char* name, npy_bool** ptr) 915 | { 916 | PyObject* array=NULL; 917 | npy_intp dims[1]; 918 | int ndims=1; 919 | if (size <= 0) { 920 | PyErr_Format(PyExc_ValueError, "size of %s array must be > 0",name); 921 | return NULL; 922 | } 923 | 924 | dims[0] = size; 925 | array = PyArray_ZEROS(ndims, dims, NPY_BOOL, 0); 926 | if (array==NULL) { 927 | PyErr_Format(PyExc_MemoryError, "could not create %s array",name); 928 | return NULL; 929 | } 930 | 931 | *ptr = PyArray_DATA((PyArrayObject*)array); 932 | return array; 933 | } 934 | 935 | 936 | static PyObject* 937 | make_intp_array(npy_intp size, const char* name, npy_intp** ptr) 938 | { 939 | PyObject* array=NULL; 940 | npy_intp dims[1]; 941 | int ndims=1; 942 | if (size <= 0) { 943 | PyErr_Format(PyExc_ValueError, "size of %s array must be > 0",name); 944 | return NULL; 945 | } 946 | 947 | dims[0] = size; 948 | array = PyArray_ZEROS(ndims, dims, NPY_INTP, 0); 949 | if (array==NULL) { 950 | PyErr_Format(PyExc_MemoryError, "could not create %s array",name); 951 | return NULL; 952 | } 953 | 954 | *ptr = PyArray_DATA((PyArrayObject*)array); 955 | return array; 956 | } 957 | 958 | static PyObject* 959 | make_longdouble_array(npy_intp size, const char* name, long double** ptr) 960 | { 961 | PyObject* array=NULL; 962 | npy_intp dims[1]; 963 | int ndims=1; 964 | if (size <= 0) { 965 | PyErr_Format(PyExc_ValueError, "size of %s array must be > 0",name); 966 | return NULL; 967 | } 968 | 969 | dims[0] = size; 970 | array = PyArray_ZEROS(ndims, dims, NPY_LONGDOUBLE, 0); 971 | if (array==NULL) { 972 | PyErr_Format(PyExc_MemoryError, "could not create %s array",name); 973 | return NULL; 974 | } 975 | 976 | *ptr = PyArray_DATA((PyArrayObject*)array); 977 | return array; 978 | } 979 | static long double* 980 | check_longdouble_array(PyObject* array, const char* name, npy_intp* size) 981 | { 982 | long double* ptr=NULL; 983 | if (!PyArray_Check(array)) { 984 | PyErr_Format(PyExc_ValueError, 985 | "%s must be a numpy array of type 64-bit float",name); 986 | return NULL; 987 | } 988 | if (NPY_LONGDOUBLE != PyArray_TYPE((PyArrayObject*)array)) { 989 | PyErr_Format(PyExc_ValueError, 990 | "%s must be a numpy array of type '128-bit' float (long double)",name); 991 | return NULL; 992 | } 993 | 994 | ptr = PyArray_DATA((PyArrayObject*)array); 995 | *size = PyArray_SIZE((PyArrayObject*)array); 996 | 997 | return ptr; 998 | } 999 | 1000 | static int 1001 | check_ra_dec_arrays(PyObject* ra_obj, PyObject* dec_obj, 1002 | long double** ra_ptr, npy_intp* nra, 1003 | long double** dec_ptr, npy_intp*ndec) 1004 | { 1005 | if (!(*ra_ptr=check_longdouble_array(ra_obj,"ra",nra))) 1006 | return 0; 1007 | if (!(*dec_ptr=check_longdouble_array(dec_obj,"dec",ndec))) 1008 | return 0; 1009 | if (*nra != *ndec) { 1010 | PyErr_Format(PyExc_ValueError, 1011 | "ra,dec must same length, got (%ld,%ld)",*nra,*ndec); 1012 | return 0; 1013 | } 1014 | 1015 | return 1; 1016 | } 1017 | 1018 | /* 1019 | * check ra,dec points, returning both poly_id and weight 1020 | * in a tuple 1021 | */ 1022 | 1023 | static PyObject* 1024 | PyMangleMask_polyid_and_weight(struct PyMangleMask* self, PyObject* args) 1025 | { 1026 | int status=1; 1027 | struct Point pt; 1028 | PyObject* ra_obj=NULL; 1029 | PyObject* dec_obj=NULL; 1030 | PyObject* poly_id_obj=NULL; 1031 | PyObject* weight_obj=NULL; 1032 | long double* ra_ptr=NULL; 1033 | long double* dec_ptr=NULL; 1034 | long double* weight_ptr=NULL; 1035 | npy_intp* poly_id_ptr=NULL; 1036 | npy_intp nra=0, ndec=0, i=0; 1037 | 1038 | PyObject* tuple=NULL; 1039 | 1040 | if (!PyArg_ParseTuple(args, (char*)"OO", &ra_obj, &dec_obj)) { 1041 | return NULL; 1042 | } 1043 | 1044 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1045 | return NULL; 1046 | } 1047 | 1048 | if (!(poly_id_obj=make_intp_array(nra, "polyid", &poly_id_ptr))) { 1049 | status=0; 1050 | goto _poly_id_and_weight_cleanup; 1051 | } 1052 | if (!(weight_obj=make_longdouble_array(nra, "weight", &weight_ptr))) { 1053 | status=0; 1054 | goto _poly_id_and_weight_cleanup; 1055 | } 1056 | 1057 | for (i=0; imask, 1061 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, 1062 | &pt, 1063 | poly_id_ptr, 1064 | weight_ptr); 1065 | 1066 | if (status != 1) { 1067 | goto _poly_id_and_weight_cleanup; 1068 | } 1069 | ra_ptr++; 1070 | dec_ptr++; 1071 | poly_id_ptr++; 1072 | weight_ptr++; 1073 | } 1074 | 1075 | _poly_id_and_weight_cleanup: 1076 | if (status != 1) { 1077 | Py_XDECREF(poly_id_obj); 1078 | Py_XDECREF(weight_obj); 1079 | Py_XDECREF(tuple); 1080 | return NULL; 1081 | } 1082 | 1083 | tuple=PyTuple_New(2); 1084 | PyTuple_SetItem(tuple, 0, poly_id_obj); 1085 | PyTuple_SetItem(tuple, 1, weight_obj); 1086 | return tuple; 1087 | } 1088 | 1089 | /* 1090 | * check ra,dec points, returning the polyid 1091 | */ 1092 | 1093 | static PyObject* 1094 | PyMangleMask_polyid(struct PyMangleMask* self, PyObject* args) 1095 | { 1096 | int status=1; 1097 | struct Point pt; 1098 | PyObject* ra_obj=NULL; 1099 | PyObject* dec_obj=NULL; 1100 | PyObject* poly_id_obj=NULL; 1101 | 1102 | long double* ra_ptr=NULL; 1103 | long double* dec_ptr=NULL; 1104 | long double weight=0; 1105 | npy_intp* poly_id_ptr=NULL; 1106 | npy_intp nra=0, ndec=0, i=0; 1107 | 1108 | if (!PyArg_ParseTuple(args, (char*)"OO", &ra_obj, &dec_obj)) { 1109 | return NULL; 1110 | } 1111 | 1112 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1113 | return NULL; 1114 | } 1115 | if (!(poly_id_obj=make_intp_array(nra, "polyid", &poly_id_ptr))) { 1116 | return NULL; 1117 | } 1118 | 1119 | for (i=0; imask, 1123 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, 1124 | &pt, 1125 | poly_id_ptr, 1126 | &weight); 1127 | 1128 | if (status != 1) { 1129 | goto _poly_id_cleanup; 1130 | } 1131 | ra_ptr++; 1132 | dec_ptr++; 1133 | poly_id_ptr++; 1134 | } 1135 | 1136 | _poly_id_cleanup: 1137 | if (status != 1) { 1138 | Py_XDECREF(poly_id_obj); 1139 | return NULL; 1140 | } 1141 | return poly_id_obj; 1142 | } 1143 | 1144 | /* 1145 | * check ra,dec points, returning weight 1146 | */ 1147 | 1148 | static PyObject* 1149 | PyMangleMask_weight(struct PyMangleMask* self, PyObject* args) 1150 | { 1151 | int status=1; 1152 | struct Point pt; 1153 | PyObject* ra_obj=NULL; 1154 | PyObject* dec_obj=NULL; 1155 | PyObject* weight_obj=NULL; 1156 | long double* ra_ptr=NULL; 1157 | long double* dec_ptr=NULL; 1158 | long double* weight_ptr=NULL; 1159 | npy_intp poly_id=0; 1160 | npy_intp nra=0, ndec=0, i=0; 1161 | 1162 | if (!PyArg_ParseTuple(args, (char*)"OO", &ra_obj, &dec_obj)) { 1163 | return NULL; 1164 | } 1165 | 1166 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1167 | return NULL; 1168 | } 1169 | if (!(weight_obj=make_longdouble_array(nra, "weight", &weight_ptr))) { 1170 | return NULL; 1171 | } 1172 | for (i=0; imask, 1176 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, 1177 | &pt, 1178 | &poly_id, 1179 | weight_ptr); 1180 | 1181 | if (status != 1) { 1182 | goto _weight_cleanup; 1183 | } 1184 | ra_ptr++; 1185 | dec_ptr++; 1186 | weight_ptr++; 1187 | } 1188 | 1189 | _weight_cleanup: 1190 | if (status != 1) { 1191 | Py_XDECREF(weight_obj); 1192 | return NULL; 1193 | } 1194 | return weight_obj; 1195 | } 1196 | 1197 | /* 1198 | * check if ra,dec points are contained 1199 | */ 1200 | 1201 | static PyObject* 1202 | PyMangleMask_contains(struct PyMangleMask* self, PyObject* args) 1203 | { 1204 | int status=1; 1205 | struct Point pt; 1206 | PyObject* ra_obj=NULL; 1207 | PyObject* dec_obj=NULL; 1208 | PyObject* contained_obj=NULL; 1209 | npy_bool* cont_ptr=NULL; 1210 | long double* ra_ptr=NULL; 1211 | long double* dec_ptr=NULL; 1212 | long double weight=0; 1213 | npy_intp poly_id=0; 1214 | npy_intp nra=0, ndec=0, i=0; 1215 | 1216 | if (!PyArg_ParseTuple(args, (char*)"OO", &ra_obj, &dec_obj)) { 1217 | return NULL; 1218 | } 1219 | 1220 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1221 | return NULL; 1222 | } 1223 | if (!(contained_obj=make_bool_array(nra, "contained", &cont_ptr))) { 1224 | return NULL; 1225 | } 1226 | 1227 | for (i=0; imask, 1231 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, 1232 | &pt, 1233 | &poly_id, 1234 | &weight); 1235 | 1236 | if (status != 1) { 1237 | goto _weight_cleanup; 1238 | } 1239 | 1240 | if (poly_id >= 0) { 1241 | *cont_ptr = 1; 1242 | } 1243 | ra_ptr++; 1244 | dec_ptr++; 1245 | cont_ptr++; 1246 | } 1247 | 1248 | _weight_cleanup: 1249 | if (status != 1) { 1250 | Py_XDECREF(contained_obj); 1251 | return NULL; 1252 | } 1253 | return contained_obj; 1254 | } 1255 | 1256 | /* 1257 | check the quadrants in the specified cap against the mask 1258 | using a monte-carlo approach 1259 | 1260 | The quadrant is considered "good" if the fraction of masked 1261 | points is less than pmax 1262 | 1263 | parameters 1264 | ---------- 1265 | ra 1266 | center of cap in degrees 1267 | dec 1268 | center of cap in degrees 1269 | angle_degrees 1270 | the opening angle of cap in degrees 1271 | amin 1272 | minimum solid angle to probe in degrees. holes larger than this area 1273 | will not be missed with probability greater than pmax 1274 | pmax 1275 | holes of size amin will be missed with probability less than pmax 1276 | 1277 | returns 1278 | ------- 1279 | maskflags 1280 | 2**0 is set if central point is inside the map 1281 | 2**1 is set if first quadrant is OK 1282 | 2**2 is set if second quadrant is OK 1283 | 2**3 is set if third quadrant is OK 1284 | 2**4 is set if fourth quadrant is OK 1285 | */ 1286 | 1287 | // number of randoms do guarantee amin is missed with probability less than 1288 | // pmax 1289 | /* 1290 | static long get_check_quad_nrand(double area, double amin, double pmax) 1291 | { 1292 | long nrand; 1293 | double pmiss; 1294 | 1295 | pmiss = 1.0 - amin/area; 1296 | if (pmiss > 1.e-10) { 1297 | // how many points do we need in order for the 1298 | // probability of missing the hole to be pmax? 1299 | // We need n such that (1-amin/a)^n = pmax 1300 | double tmp = log10(pmax)/log10(pmiss); 1301 | if (tmp < 20) tmp = 20; 1302 | if (tmp > 20000) tmp = 20000; 1303 | nrand = lround(tmp); 1304 | } else { 1305 | // we reach here often because the search area is very 1306 | // close to or smaller than our minimum resolvable area 1307 | // We don't want nrand to be less than say 20 1308 | nrand = 20; 1309 | } 1310 | 1311 | return nrand; 1312 | } 1313 | */ 1314 | 1315 | // generate random points, return the fraction that were masked 1316 | static double get_quad_frac_masked(struct PyMangleMask* self, 1317 | long nrand, 1318 | const struct CapForRand *rcap, 1319 | int quadrant) 1320 | { 1321 | int status=1; 1322 | long nmasked=0, i; 1323 | double frac_masked=0; 1324 | long double ra=0, dec=0, weight=0; 1325 | int64 poly_id=0; 1326 | struct Point pt={0}; 1327 | 1328 | for (i=0; imask, 1333 | &pt, 1334 | &poly_id, 1335 | &weight); 1336 | 1337 | if (poly_id < 0 || weight <= 0.) { 1338 | nmasked += 1; 1339 | } 1340 | } 1341 | 1342 | frac_masked = ( (double)nmasked )/( (double)nrand ); 1343 | return frac_masked; 1344 | } 1345 | 1346 | static PyObject* 1347 | PyMangleMask_check_quadrants(struct PyMangleMask* self, PyObject* args) 1348 | { 1349 | int status=1; 1350 | 1351 | PyObject* ra_obj=NULL; 1352 | PyObject* dec_obj=NULL; 1353 | PyObject* angle_degrees_obj=NULL; 1354 | PyObject* maskflags_obj=NULL; 1355 | npy_intp nra=0, ndec=0, nang=0, i=0, *maskflags_ptr=NULL; 1356 | 1357 | long double* ra_ptr=NULL; 1358 | long double* dec_ptr=NULL; 1359 | long double* ang_ptr=NULL; 1360 | 1361 | double density_degrees, 1362 | max_masked_fraction, frac_masked; 1363 | double area; 1364 | struct Point pt; 1365 | long double weight; 1366 | int64 poly_id; 1367 | struct CapForRand rcap; 1368 | 1369 | long nrand; 1370 | int mask_flags=0; 1371 | 1372 | if (!PyArg_ParseTuple(args, (char*)"OOOdd", 1373 | &ra_obj, 1374 | &dec_obj, 1375 | &angle_degrees_obj, 1376 | &density_degrees, // number of randoms/square degree 1377 | &max_masked_fraction)) { 1378 | return NULL; 1379 | } 1380 | 1381 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1382 | return NULL; 1383 | } 1384 | if (!check_ra_dec_arrays(ra_obj,angle_degrees_obj,&ra_ptr,&nra,&ang_ptr,&nang)) { 1385 | return NULL; 1386 | } 1387 | if (!(maskflags_obj=make_intp_array(nra, "maskflags", &maskflags_ptr))) { 1388 | return NULL; 1389 | } 1390 | 1391 | 1392 | // area of a quadrant = 1/4 pi r^2 1393 | for (i=0; imask, 1406 | &pt, 1407 | &poly_id, 1408 | &weight); 1409 | 1410 | if (poly_id >= 0) { 1411 | int quadrant; 1412 | mask_flags |= 1; 1413 | 1414 | CapForRand_from_radec(&rcap, ra_cen, dec_cen, ang); 1415 | 1416 | for (quadrant=1; quadrant <= 4; quadrant++) { 1417 | frac_masked = get_quad_frac_masked(self, nrand, &rcap, quadrant); 1418 | if (frac_masked < max_masked_fraction) { 1419 | mask_flags |= (1< 0, got (%ld)",(npy_intp)nrand); 1463 | status=0; 1464 | goto _genrand_cleanup; 1465 | } 1466 | 1467 | if (!(ra_obj=make_longdouble_array(nrand, "ra", &ra_ptr))) { 1468 | status=0; 1469 | goto _genrand_cleanup; 1470 | } 1471 | if (!(dec_obj=make_longdouble_array(nrand, "dec", &dec_ptr))) { 1472 | status=0; 1473 | goto _genrand_cleanup; 1474 | } 1475 | 1476 | seed_random(); 1477 | 1478 | while (ngood < nrand) { 1479 | genrand_theta_phi_allsky(&theta, &phi); 1480 | point_set_from_thetaphi(&pt, theta, phi); 1481 | 1482 | //status=mangle_polyid_and_weight(self->mask, 1483 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, 1484 | &pt, 1485 | &poly_id, 1486 | &weight); 1487 | 1488 | if (status != 1) { 1489 | goto _genrand_cleanup; 1490 | } 1491 | 1492 | if (poly_id >= 0) { 1493 | // rely on short circuiting 1494 | if (weight < 1.0 || drand48() < weight) { 1495 | ngood++; 1496 | radec_from_point(&pt, ra_ptr, dec_ptr); 1497 | ra_ptr++; 1498 | dec_ptr++; 1499 | } 1500 | } 1501 | } 1502 | 1503 | _genrand_cleanup: 1504 | if (status != 1) { 1505 | Py_XDECREF(ra_obj); 1506 | Py_XDECREF(dec_obj); 1507 | Py_XDECREF(tuple); 1508 | return NULL; 1509 | } 1510 | 1511 | tuple=PyTuple_New(2); 1512 | PyTuple_SetItem(tuple, 0, ra_obj); 1513 | PyTuple_SetItem(tuple, 1, dec_obj); 1514 | return tuple; 1515 | } 1516 | 1517 | 1518 | 1519 | /* 1520 | * Generate random points in the input ra,dec range. 1521 | * 1522 | * Use this if you have a small mask, as choosing the range wisely can save a 1523 | * lot of time. But be careful: if you choose your range poorly, it may never 1524 | * find the points! 1525 | */ 1526 | 1527 | static PyObject* 1528 | PyMangleMask_genrand_range(struct PyMangleMask* self, PyObject* args) 1529 | { 1530 | int status=1; 1531 | PY_LONG_LONG nrand=0; 1532 | double ramin=0,ramax=0,decmin=0,decmax=0; 1533 | long double cthmin=0,cthmax=0,phimin=0,phimax=0; 1534 | struct Point pt; 1535 | PyObject* ra_obj=NULL; 1536 | PyObject* dec_obj=NULL; 1537 | PyObject* tuple=NULL; 1538 | long double* ra_ptr=NULL; 1539 | long double* dec_ptr=NULL; 1540 | long double weight=0; 1541 | npy_intp poly_id=0; 1542 | npy_intp ngood=0; 1543 | int num_contained=0; 1544 | long double theta=0, phi=0; 1545 | 1546 | 1547 | if (!PyArg_ParseTuple(args, (char*)"Ldddd", 1548 | &nrand, &ramin, &ramax, &decmin, &decmax)) { 1549 | return NULL; 1550 | } 1551 | 1552 | if (nrand <= 0) { 1553 | PyErr_Format(PyExc_ValueError, 1554 | "nrand should be > 0, got (%ld)",(npy_intp)nrand); 1555 | status=0; 1556 | goto _genrand_range_cleanup; 1557 | } 1558 | if (!radec_range_to_costhetaphi((long double)ramin,(long double)ramax, 1559 | (long double)decmin,(long double)decmax, 1560 | &cthmin,&cthmax,&phimin,&phimax)) { 1561 | status=0; 1562 | goto _genrand_range_cleanup; 1563 | } 1564 | 1565 | point_set_from_radec(&pt, ramin, decmin); 1566 | point_set_from_radec(&pt, ramin, decmax); 1567 | point_set_from_radec(&pt, ramax, decmin); 1568 | point_set_from_radec(&pt, ramax, decmax); 1569 | 1570 | if (!(ra_obj=make_longdouble_array(nrand, "ra", &ra_ptr))) { 1571 | status=0; 1572 | goto _genrand_range_cleanup; 1573 | } 1574 | if (!(dec_obj=make_longdouble_array(nrand, "dec", &dec_ptr))) { 1575 | status=0; 1576 | goto _genrand_range_cleanup; 1577 | } 1578 | 1579 | seed_random(); 1580 | 1581 | while (ngood < nrand) { 1582 | genrand_theta_phi(cthmin,cthmax,phimin,phimax,&theta, &phi); 1583 | point_set_from_thetaphi(&pt, theta, phi); 1584 | 1585 | //status=mangle_polyid_and_weight(self->mask, &pt, &poly_id, &weight); 1586 | status=MANGLE_POLYID_AND_WEIGHT(self->mask, &pt, &poly_id, &weight); 1587 | 1588 | if (status != 1) { 1589 | goto _genrand_range_cleanup; 1590 | } 1591 | 1592 | if (poly_id >= 0) { 1593 | // rely on short circuiting 1594 | if (weight < 1.0 || drand48() < weight) { 1595 | ngood++; 1596 | radec_from_point(&pt, ra_ptr, dec_ptr); 1597 | ra_ptr++; 1598 | dec_ptr++; 1599 | } 1600 | } 1601 | } 1602 | 1603 | _genrand_range_cleanup: 1604 | if (status != 1) { 1605 | Py_XDECREF(ra_obj); 1606 | Py_XDECREF(dec_obj); 1607 | Py_XDECREF(tuple); 1608 | return NULL; 1609 | } 1610 | 1611 | tuple=PyTuple_New(2); 1612 | PyTuple_SetItem(tuple, 0, ra_obj); 1613 | PyTuple_SetItem(tuple, 1, dec_obj); 1614 | return tuple; 1615 | } 1616 | 1617 | 1618 | static PyObject * 1619 | PyMangleMask_pixels(struct PyMangleMask* self) { 1620 | int status=1; 1621 | PyObject *pixel_obj=NULL; 1622 | npy_intp *pixel_ptr=NULL; 1623 | struct Polygon *ply=NULL; 1624 | npy_intp i; 1625 | 1626 | if (!(pixel_obj=make_intp_array(self->mask->poly_vec->size,"pixels", &pixel_ptr))) { 1627 | status = 0; 1628 | goto _pixels_cleanup; 1629 | } 1630 | 1631 | ply = &self->mask->poly_vec->data[0]; 1632 | for (i=0;imask->poly_vec->size;i++) { 1633 | pixel_ptr[i] = ply->pixel_id; 1634 | ply++; 1635 | } 1636 | 1637 | _pixels_cleanup: 1638 | if (status != 1) { 1639 | Py_XDECREF(pixel_obj); 1640 | return NULL; 1641 | } 1642 | 1643 | return PyArray_Return((PyArrayObject *)pixel_obj); 1644 | } 1645 | 1646 | static PyObject * 1647 | PyMangleMask_weights(struct PyMangleMask* self) { 1648 | int status=1; 1649 | PyObject *weight_obj=NULL; 1650 | long double *weight_ptr=NULL; 1651 | struct Polygon *ply=NULL; 1652 | npy_intp i; 1653 | 1654 | if (!(weight_obj=make_longdouble_array(self->mask->poly_vec->size,"weight", &weight_ptr))) { 1655 | status = 0; 1656 | goto _weights_cleanup; 1657 | } 1658 | 1659 | ply = &self->mask->poly_vec->data[0]; 1660 | for (i=0;imask->poly_vec->size;i++) { 1661 | weight_ptr[i] = ply->weight; 1662 | ply++; 1663 | } 1664 | 1665 | _weights_cleanup: 1666 | if (status != 1) { 1667 | Py_XDECREF(weight_obj); 1668 | return NULL; 1669 | } 1670 | 1671 | return PyArray_Return((PyArrayObject *)weight_obj); 1672 | } 1673 | 1674 | static PyObject * 1675 | PyMangleMask_areas(struct PyMangleMask* self) { 1676 | int status=1; 1677 | PyObject *area_obj=NULL; 1678 | long double *area_ptr=NULL; 1679 | struct Polygon *ply=NULL; 1680 | npy_intp i; 1681 | 1682 | if (!(area_obj=make_longdouble_array(self->mask->poly_vec->size,"area", &area_ptr))) { 1683 | status = 0; 1684 | goto _areas_cleanup; 1685 | } 1686 | 1687 | ply = &self->mask->poly_vec->data[0]; 1688 | for (i=0;imask->poly_vec->size;i++) { 1689 | area_ptr[i] = ply->area*R2D*R2D; 1690 | ply++; 1691 | } 1692 | 1693 | _areas_cleanup: 1694 | if (status != 1) { 1695 | Py_XDECREF(area_obj); 1696 | return NULL; 1697 | } 1698 | 1699 | return PyArray_Return((PyArrayObject *)area_obj); 1700 | } 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | static PyObject * 1707 | PyMangleMask_calc_simplepix(struct PyMangleMask *self, PyObject *args) { 1708 | struct Point pt; 1709 | PyObject *ra_obj=NULL; 1710 | PyObject *dec_obj=NULL; 1711 | PyObject *simplepix_obj=NULL; 1712 | 1713 | long double *ra_ptr=NULL; 1714 | long double *dec_ptr=NULL; 1715 | npy_intp *simplepix_ptr=NULL; 1716 | npy_intp nra=0, ndec=0, i=0; 1717 | 1718 | if (!PyArg_ParseTuple(args, (char*)"OO", &ra_obj, &dec_obj)) { 1719 | return NULL; 1720 | } 1721 | 1722 | if (self->mask->pixeltype == 'u') { 1723 | // not pixelized! 1724 | PyErr_SetString(PyExc_ValueError,"Must be a pixelized file"); 1725 | return NULL; 1726 | } 1727 | 1728 | if (!check_ra_dec_arrays(ra_obj,dec_obj,&ra_ptr,&nra,&dec_ptr,&ndec)) { 1729 | return NULL; 1730 | } 1731 | if (!(simplepix_obj=make_intp_array(nra, "simplepix", &simplepix_ptr))) { 1732 | return NULL; 1733 | } 1734 | 1735 | for (i=0; imask->pixelres,&pt); 1739 | 1740 | ra_ptr++; 1741 | dec_ptr++; 1742 | simplepix_ptr++; 1743 | } 1744 | 1745 | return simplepix_obj; 1746 | } 1747 | 1748 | 1749 | static PyMethodDef PyMangleMask_methods[] = { 1750 | {"polyid_and_weight", (PyCFunction)PyMangleMask_polyid_and_weight, METH_VARARGS, 1751 | "polyid_and_weight(ra,dec)\n" 1752 | "\n" 1753 | "Check points against mask, returning (poly_id,weight).\n" 1754 | "\n" 1755 | "parameters\n" 1756 | "----------\n" 1757 | "ra: array\n" 1758 | " A numpy array of type 'f16'\n" 1759 | "dec: array\n" 1760 | " A numpy array of type 'f16'\n"}, 1761 | {"polyid", (PyCFunction)PyMangleMask_polyid, METH_VARARGS, 1762 | "polyid(ra,dec)\n" 1763 | "\n" 1764 | "Check points against mask, returning the polygon id or -1.\n" 1765 | "\n" 1766 | "parameters\n" 1767 | "----------\n" 1768 | "ra: array\n" 1769 | " A numpy array of type 'f16'\n" 1770 | "dec: array\n" 1771 | " A numpy array of type 'f16'\n"}, 1772 | {"weight", (PyCFunction)PyMangleMask_weight, METH_VARARGS, 1773 | "weight(ra,dec)\n" 1774 | "\n" 1775 | "Check points against mask, returning the weight or 0.0\n" 1776 | "\n" 1777 | "parameters\n" 1778 | "----------\n" 1779 | "ra: array\n" 1780 | " A numpy array of type 'f16'\n" 1781 | "dec: array\n" 1782 | " A numpy array of type 'f16'\n"}, 1783 | {"contains", (PyCFunction)PyMangleMask_contains, METH_VARARGS, 1784 | "contains(ra,dec)\n" 1785 | "\n" 1786 | "Check points against mask, returning 1 if contained 0 if not\n" 1787 | "\n" 1788 | "parameters\n" 1789 | "----------\n" 1790 | "ra: array\n" 1791 | " A numpy array of type 'f16'\n" 1792 | "dec: array\n" 1793 | " A numpy array of type 'f16'\n"}, 1794 | 1795 | {"check_quadrants", (PyCFunction)PyMangleMask_check_quadrants, METH_VARARGS, 1796 | "check_quadrants(ra,dec)\n" 1797 | "\n" 1798 | "check quadrants of a cap against the mask\n"}, 1799 | 1800 | 1801 | {"genrand", (PyCFunction)PyMangleMask_genrand, METH_VARARGS, 1802 | "genrand(nrand)\n" 1803 | "\n" 1804 | "Generate random points that are within the mask.\n" 1805 | "\n" 1806 | "parameters\n" 1807 | "----------\n" 1808 | "nrand: number\n" 1809 | " The number of random points to generate\n" 1810 | "\n" 1811 | "output\n" 1812 | "------\n" 1813 | "ra,dec arrays"}, 1814 | 1815 | {"genrand_range", (PyCFunction)PyMangleMask_genrand_range, METH_VARARGS, 1816 | "genrand_range(nrand,ramin,ramax,decmin,decmax)\n" 1817 | "\n" 1818 | "Generate random points inside the input range and the mask.\n" 1819 | "The use case is when the mask area is small compared to the\n" 1820 | "entire sphere; test points can be drawn from a region around \n" 1821 | "the mask rather than the full sphere, improving speed.\n" 1822 | "\n" 1823 | "parameters\n" 1824 | "----------\n" 1825 | "nrand: number\n" 1826 | " The number of random points to generate\n" 1827 | "ramin: double\n" 1828 | " The minimum ra\n" 1829 | "ramax: double\n" 1830 | " The maximum ra\n" 1831 | "decmin: double\n" 1832 | " The minimum dec\n" 1833 | "decmax: double\n" 1834 | " The maximum dec\n" 1835 | "\n" 1836 | "output\n" 1837 | "------\n" 1838 | "ra,dec arrays"}, 1839 | 1840 | {"read_weights", (PyCFunction)PyMangleMask_read_weights, METH_VARARGS, 1841 | "read_weights(weightfile)\n" 1842 | "\n" 1843 | "Read weights from a separate weightfile.\n"}, 1844 | {"set_weights", (PyCFunction)PyMangleMask_set_weights, METH_VARARGS, 1845 | "set_weights(weights)\n" 1846 | "\n" 1847 | "Set weights for all polygons.\n"}, 1848 | {"get_filename", (PyCFunction)PyMangleMask_filename, METH_VARARGS, 1849 | "filename()\n" 1850 | "\n" 1851 | "Return the mask filename.\n"}, 1852 | 1853 | {"get_weightfile", (PyCFunction)PyMangleMask_weightfile, METH_VARARGS, 1854 | "weightfile()\n" 1855 | "\n" 1856 | "Return the weight filename.\n"}, 1857 | 1858 | 1859 | {"get_area", (PyCFunction)PyMangleMask_area, METH_VARARGS, 1860 | "area()\n" 1861 | "\n" 1862 | "Return the area within mask in square degrees.\n"}, 1863 | 1864 | {"get_npoly", (PyCFunction)PyMangleMask_npoly, METH_VARARGS, 1865 | "npoly()\n" 1866 | "\n" 1867 | "Return the polygon count.\n"}, 1868 | 1869 | {"get_is_pixelized", (PyCFunction)PyMangleMask_is_pixelized, METH_VARARGS, 1870 | "get_is_pixelized()\n" 1871 | "\n" 1872 | "True if pixelized.\n"}, 1873 | {"get_maxpix", (PyCFunction)PyMangleMask_maxpix, METH_VARARGS, 1874 | "maxpix()\n" 1875 | "\n" 1876 | "Return the maximum pixel id.\n"}, 1877 | {"get_pixelres", (PyCFunction)PyMangleMask_pixelres, METH_VARARGS, 1878 | "pixelres()\n" 1879 | "\n" 1880 | "Return the pixel resolution, -1 if unpixelized.\n"}, 1881 | {"get_pixeltype", (PyCFunction)PyMangleMask_pixeltype, METH_VARARGS, 1882 | "pixeltype()\n" 1883 | "\n" 1884 | "Return the pixel type as a string. 'u' if unpixelized.\n"}, 1885 | 1886 | 1887 | 1888 | {"get_is_snapped", (PyCFunction)PyMangleMask_is_snapped, METH_VARARGS, 1889 | "get_is_snapped()\n" 1890 | "\n" 1891 | "Return True if the snapped keyword was found in the header.\n"}, 1892 | {"get_is_balkanized", (PyCFunction)PyMangleMask_is_balkanized, METH_VARARGS, 1893 | "get_is_balkanized()\n" 1894 | "\n" 1895 | "Return True if the balkanized keyword was found in the header.\n"}, 1896 | {"get_pixels", (PyCFunction)PyMangleMask_pixels, METH_VARARGS, 1897 | "get_pixels()\n" 1898 | "\n" 1899 | "Return the array of pixels in the input file.\n"}, 1900 | {"get_weights", (PyCFunction)PyMangleMask_weights, METH_VARARGS, 1901 | "get_weights()\n" 1902 | "\n" 1903 | "Return the array of weights in the input file.\n"}, 1904 | {"get_areas", (PyCFunction)PyMangleMask_areas, METH_VARARGS, 1905 | "get_areas()\n" 1906 | "\n" 1907 | "Return the array of areas in the input file (in square degrees).\n"}, 1908 | {"calc_simplepix", (PyCFunction)PyMangleMask_calc_simplepix, METH_VARARGS, 1909 | "calc_simplepix(ra,dec)\n" 1910 | "\n" 1911 | "Calculate simple pixel numbers, given pixelization scheme.\n" 1912 | "\n" 1913 | "parameters\n" 1914 | "----------\n" 1915 | "ra: array\n" 1916 | " A numpy array of type 'f16'\n" 1917 | "dec: array\n" 1918 | " A numpy array of type 'f16'\n"}, 1919 | {NULL} /* Sentinel */ 1920 | }; 1921 | 1922 | 1923 | static PyTypeObject PyMangleMaskType = { 1924 | #if PY_MAJOR_VERSION >= 3 1925 | PyVarObject_HEAD_INIT(NULL, 0) 1926 | #else 1927 | PyObject_HEAD_INIT(NULL) 1928 | 0, /*ob_size*/ 1929 | #endif 1930 | "_mangle.Mangle", /*tp_name*/ 1931 | sizeof(struct PyMangleMask), /*tp_basicsize*/ 1932 | 0, /*tp_itemsize*/ 1933 | (destructor)PyMangleMask_dealloc, /*tp_dealloc*/ 1934 | 0, /*tp_print*/ 1935 | 0, /*tp_getattr*/ 1936 | 0, /*tp_setattr*/ 1937 | 0, /*tp_compare*/ 1938 | //0, /*tp_repr*/ 1939 | (reprfunc)PyMangleMask_repr, /*tp_repr*/ 1940 | 0, /*tp_as_number*/ 1941 | 0, /*tp_as_sequence*/ 1942 | 0, /*tp_as_mapping*/ 1943 | 0, /*tp_hash */ 1944 | 0, /*tp_call*/ 1945 | 0, /*tp_str*/ 1946 | 0, /*tp_getattro*/ 1947 | 0, /*tp_setattro*/ 1948 | 0, /*tp_as_buffer*/ 1949 | Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 1950 | "A class to work with Mangle masks.\n" 1951 | "\n" 1952 | "construction\n" 1953 | " import pymangle\n" 1954 | " m=pymangle.Mangle(mask_file, verbose=False)\n" 1955 | "\n" 1956 | "\n" 1957 | "read-only properties\n" 1958 | "--------------------\n" 1959 | " filename\n" 1960 | " area\n" 1961 | " npoly\n" 1962 | " is_pixelized\n" 1963 | " pixeltype\n" 1964 | " pixelres\n" 1965 | " maxpix\n" 1966 | " is_snapped\n" 1967 | " is_balkanized\n" 1968 | " weightfile\n" 1969 | "\n" 1970 | "See docs for each property for more details.\n" 1971 | "\n" 1972 | "methods\n" 1973 | "----------------------\n" 1974 | " polyid(ra,dec)\n" 1975 | " weight(ra,dec)\n" 1976 | " polyid_and_weight(ra,dec)\n" 1977 | " contains(ra,dec)\n" 1978 | " genrand(nrand)\n" 1979 | " genrand_range(nrand,ramin,ramax,decmin,decmax)\n" 1980 | " calc_simplepix(ra,dec)\n" 1981 | " read_weights(weightfile)\n" 1982 | "\n" 1983 | "getters (correspond to properties above)\n" 1984 | "----------------------------------------\n" 1985 | " get_filename()\n" 1986 | " get_weightfile()\n" 1987 | " get_area()\n" 1988 | " get_npoly()\n" 1989 | " get_is_pixelized()\n" 1990 | " get_pixeltype()\n" 1991 | " get_pixelres()\n" 1992 | " get_maxpix()\n" 1993 | " get_is_snapped()\n" 1994 | " get_is_balkanized()\n" 1995 | " get_pixels()\n" 1996 | " get_weights()\n" 1997 | " get_areas()\n" 1998 | "\n" 1999 | "setter (corresponding to property above)\n" 2000 | "----------------------------------------\n" 2001 | " set_weights(weights)\n" 2002 | "\n" 2003 | "See docs for each method for more detail.\n", 2004 | 0, /* tp_traverse */ 2005 | 0, /* tp_clear */ 2006 | 0, /* tp_richcompare */ 2007 | 0, /* tp_weaklistoffset */ 2008 | 0, /* tp_iter */ 2009 | 0, /* tp_iternext */ 2010 | PyMangleMask_methods, /* tp_methods */ 2011 | 0, /* tp_members */ 2012 | 0, /* tp_getset */ 2013 | 0, /* tp_base */ 2014 | 0, /* tp_dict */ 2015 | 0, /* tp_descr_get */ 2016 | 0, /* tp_descr_set */ 2017 | 0, /* tp_dictoffset */ 2018 | //0, /* tp_init */ 2019 | (initproc)PyMangleMask_init, /* tp_init */ 2020 | 0, /* tp_alloc */ 2021 | PyType_GenericNew, /* tp_new */ 2022 | }; 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 | /* 2030 | * Generate random points in the specified cap 2031 | * 2032 | * This is a straight function, not a method of a mangle mask 2033 | * 2034 | */ 2035 | 2036 | static PyObject* 2037 | PyMangle_genrand_cap(PyObject* self, PyObject* args) 2038 | { 2039 | int status=1; 2040 | PY_LONG_LONG nrand=0, i=0; 2041 | double ra_cen=0,dec_cen=0,angle_degrees=0; 2042 | int quadrant; 2043 | PyObject* ra_obj=NULL; 2044 | PyObject* dec_obj=NULL; 2045 | PyObject* tuple=NULL; 2046 | long double* ra_ptr=NULL; 2047 | long double* dec_ptr=NULL; 2048 | struct CapForRand rcap; 2049 | 2050 | if (!PyArg_ParseTuple(args, (char*)"Ldddi", 2051 | &nrand, &ra_cen, &dec_cen, &angle_degrees, &quadrant)) { 2052 | return NULL; 2053 | } 2054 | 2055 | if (nrand <= 0) { 2056 | PyErr_Format(PyExc_ValueError, 2057 | "nrand should be > 0, got (%ld)",(npy_intp)nrand); 2058 | status=0; 2059 | goto genrand_cap_cleanup; 2060 | } 2061 | if (angle_degrees <= 0 || angle_degrees > 180.) { 2062 | PyErr_Format(PyExc_ValueError, 2063 | "angle_degrees should in [0,180), got (%ld)",(long)angle_degrees); 2064 | status=0; 2065 | goto genrand_cap_cleanup; 2066 | } 2067 | 2068 | 2069 | if (!(ra_obj=make_longdouble_array(nrand, "ra", &ra_ptr))) { 2070 | status=0; 2071 | goto genrand_cap_cleanup; 2072 | } 2073 | if (!(dec_obj=make_longdouble_array(nrand, "dec", &dec_ptr))) { 2074 | status=0; 2075 | goto genrand_cap_cleanup; 2076 | } 2077 | 2078 | seed_random(); 2079 | 2080 | CapForRand_from_radec(&rcap, ra_cen, dec_cen, angle_degrees); 2081 | 2082 | for (i=0; i= 3 2114 | static struct PyModuleDef moduledef = { 2115 | PyModuleDef_HEAD_INIT, 2116 | "_mangle", /* m_name */ 2117 | "Defines Mangle related classes and some methods", /* m_doc */ 2118 | -1, /* m_size */ 2119 | mangle_methods, /* m_methods */ 2120 | NULL, /* m_reload */ 2121 | NULL, /* m_traverse */ 2122 | NULL, /* m_clear */ 2123 | NULL, /* m_free */ 2124 | }; 2125 | #endif 2126 | 2127 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 2128 | #define PyMODINIT_FUNC void 2129 | #endif 2130 | PyMODINIT_FUNC 2131 | #if PY_MAJOR_VERSION >= 3 2132 | PyInit__mangle(void) 2133 | #else 2134 | init_mangle(void) 2135 | #endif 2136 | { 2137 | PyObject* m; 2138 | 2139 | 2140 | PyMangleCapType.tp_new = PyType_GenericNew; 2141 | PyMangleCapVecType.tp_new = PyType_GenericNew; 2142 | PyManglePolygonType.tp_new = PyType_GenericNew; 2143 | PyMangleMaskType.tp_new = PyType_GenericNew; 2144 | 2145 | #if PY_MAJOR_VERSION >= 3 2146 | if (PyType_Ready(&PyMangleCapType) < 0) { 2147 | return NULL; 2148 | } 2149 | if (PyType_Ready(&PyMangleCapVecType) < 0) { 2150 | return NULL; 2151 | } 2152 | if (PyType_Ready(&PyManglePolygonType) < 0) { 2153 | return NULL; 2154 | } 2155 | if (PyType_Ready(&PyMangleMaskType) < 0) { 2156 | return NULL; 2157 | } 2158 | m = PyModule_Create(&moduledef); 2159 | if (m==NULL) { 2160 | return NULL; 2161 | } 2162 | 2163 | #else 2164 | if (PyType_Ready(&PyMangleMaskType) < 0) { 2165 | return; 2166 | } 2167 | if (PyType_Ready(&PyMangleCapType) < 0) { 2168 | return; 2169 | } 2170 | if (PyType_Ready(&PyMangleCapVecType) < 0) { 2171 | return; 2172 | } 2173 | if (PyType_Ready(&PyManglePolygonType) < 0) { 2174 | return; 2175 | } 2176 | m = Py_InitModule3("_mangle", mangle_methods, 2177 | "This module defines a class to work with Mangle masks.\n" 2178 | "and some generic functions.\n" 2179 | "See docs for pymangle.Mangle for more details\n"); 2180 | if (m==NULL) { 2181 | return; 2182 | } 2183 | #endif 2184 | 2185 | Py_INCREF(&PyMangleMaskType); 2186 | PyModule_AddObject(m, "Cap", (PyObject *)&PyMangleCapType); 2187 | PyModule_AddObject(m, "CapVec", (PyObject *)&PyMangleCapVecType); 2188 | PyModule_AddObject(m, "Polygon", (PyObject *)&PyManglePolygonType); 2189 | PyModule_AddObject(m, "Mangle", (PyObject *)&PyMangleMaskType); 2190 | 2191 | import_array(); 2192 | #if PY_MAJOR_VERSION >= 3 2193 | return m; 2194 | #endif 2195 | } 2196 | --------------------------------------------------------------------------------