{"id":17719953,"url":"https://github.com/evanmiller/projcl","last_synced_at":"2025-04-30T07:50:01.357Z","repository":{"id":4033901,"uuid":"5134709","full_name":"evanmiller/ProjCL","owner":"evanmiller","description":"GPU and vector-enabled map projections, geodesic calculations, and image warping 🌎🌍🌏","archived":false,"fork":false,"pushed_at":"2020-12-24T15:55:05.000Z","size":202,"stargazers_count":91,"open_issues_count":4,"forks_count":11,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-30T07:49:52.108Z","etag":null,"topics":["geo","map-projections","opencl"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evanmiller.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-07-21T15:41:36.000Z","updated_at":"2025-04-27T10:04:30.000Z","dependencies_parsed_at":"2022-08-06T14:30:36.597Z","dependency_job_id":null,"html_url":"https://github.com/evanmiller/ProjCL","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmiller%2FProjCL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmiller%2FProjCL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmiller%2FProjCL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evanmiller%2FProjCL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evanmiller","download_url":"https://codeload.github.com/evanmiller/ProjCL/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251666227,"owners_count":21624291,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["geo","map-projections","opencl"],"created_at":"2024-10-25T15:18:52.642Z","updated_at":"2025-04-30T07:50:01.332Z","avatar_url":"https://github.com/evanmiller.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub Actions build](https://github.com/evanmiller/ProjCL/workflows/build/badge.svg)](https://github.com/evanmiller/ProjCL/actions)\n\nProjCL: OpenCL-powered map projection, geodesic, and image-warping library\n==\n\nProjCL is a C interface to [OpenCL](https://en.wikipedia.org/wiki/OpenCL)\nroutines that perform various geographic computations, including map\nprojection, geodesic (distance) calculations, datum conversion, and basic image\nwarping (reprojection). For projection calculations it is often 4-10X faster\nthan [Proj.4](http://proj4.org) on the CPU, and 15-30X faster on a low-end GPU\nwith large batches of coordinates. For datum shifts ProjCL is smarter than\nProj.4 because it does some matrix math in advance, and generally faster\nbecause OpenCL can utilize all cores and the CPU's vector capabilities.\n\nMost projection routines were originally adapted from Proj.4 code, with\nbranches replaced with select() statements and various tweaks implemented along\nthe way. The library was developed to support real-time map projections in\n[Magic Maps](https://magicmaps.evanmiller.org/).\n\nAll of the routines are single-precision. In theory, single-precision floats \ncan represent positions on the Earth with about 1m of accuracy. In practice,\nthe test suite guarantees 10 meters of accuracy in projected coordinates,\nand one arc-second of accuracy in geodetic coordinates (about 30 meters at\nthe Equator). Most routines have been carefully written to avoid round-off\nerror, and have at least twice the accuracy that these guarantees imply.\n\nDouble-precision should probably be implemented at some point, but it hasn't\nbeen a priority.\n\nThe API differs from Proj.4 in that projections are specified using compile-time\nconstants, and parameters are specified using a dedicated data structure.\nText-based C APIs like Proj.4's are prone to error in my experience.\n\nA test suite covers the projection routines, but not the geodesic calculations,\ndatum shifts, or image warping. The output is checked for self-consistency, as\nwell as agreement with Proj.4. The code is tested to work on OS X as well as\nLinux; when making a pull request, please ensure all the tests pass with\n`./test/projcl_test -CPU`.\n\nProjCL needs more map projections. In fact, the world needs more map projections.\nIf you want to try your hand at one, check out \"Adding a Map Projection\" below.\n\nAvailable projections:\n* Albers Equal Area\n* American Polyconic\n* Lambert Azimuthal Equal Area\n* Lambert Conformal Conic\n* Mercator\n* Robinson\n* Transverse Mercator\n* Winkel Tripel\n\nAvailable datums and spheroids: see [include/projcl/projcl_types.h](https://github.com/evanmiller/ProjCL/blob/master/include/projcl/projcl_types.h)\n\nAvailable image filters: see [include/projcl/projcl_warp.h](https://github.com/evanmiller/ProjCL/blob/master/include/projcl/projcl_warp.h)\n\nBuilding\n--\n\nProjCL requires [CMake](http://www.cmake.org) build system. To build the library do:\n\n```\n$ cmake CMakeLists.txt\n$ make\t\n```\n\nSetup\n--\n\n```{C}\n#include \u003cprojcl/projcl.h\u003e\n\ncl_int error = CL_SUCCESS;\n\nPLContext *ctx = pl_context_init(CL_DEVICE_TYPE_CPU, \u0026error);\n\nPLCode *code = pl_compile_code(ctx, \"/path/to/ProjCL/kernel\", \n        PL_MODULE_DATUM \n        | PL_MODULE_GEODESIC \n        | PL_MODULE_PROJECTION\n        | PL_MODULE_WARP \n        | PL_MODULE_FILTER);\n\nerror = pl_load_code(ctx, code);\n```\n\nTeardown\n--\n\n```{C}\npl_unload_code(ctx);\npl_release_code(code);\npl_context_free(ctx);\n```\n\nForward projection\n--\n\n```{C}\n/* get the input data from somewhere */\n/* latitude-longitude pairs */\nint count = ...;\nfloat *lat_lon_data = ...;\n\n/* load point data */\nPLProjectionBuffer *proj_buffer = pl_load_projection_data(ctx, lat_lon_data, count, 1, \u0026error);\n\n/* allocate output buffer */\nfloat *xy_data = malloc(2 * count * sizeof(float));\n\n/* Set some params */\nPLProjectionParams *params = pl_params_init();\npl_params_set_scale(1.0);\npl_params_set_spheroid(PL_SPHEROID_WGS_84);\npl_params_set_false_northing(0.0);\npl_params_set_false_easting(0.0);\n\n/* project forwards */\nerror = pl_project_points_forward(ctx, PL_PROJECT_MERCATOR, params, proj_buffer, xy_data);\n\n/* unload */\npl_unload_projection_data(proj_buffer);\npl_params_free(params);\n```\n\nInverse projection\n--\n\n```{C}\n/* get the input data from somewhere */\n/* X-Y pairs */\nint count = ...;\nfloat *xy_data = ...;\n\nPLProjectionBuffer *cartesian_buffer = pl_load_projection_data(ctx, xy_data, count, 1, \u0026error);\n\nfloat *lat_lon_data = malloc(2 * count * sizeof(float));\n\nPLProjectionParams *params = pl_params_init();\npl_params_set_scale(1.0);\npl_params_set_spheroid(PL_SPHEROID_WGS_84);\npl_params_set_false_northing(0.0);\npl_params_set_false_easting(0.0);\n\nerror = pl_project_points_reverse(ctx, PL_PROJECT_MERCATOR, params, cartesian_buffer, lat_lon_data);\n\npl_unload_projection_data(cartesian_buffer);\n```\n\nDatum shift\n--\n\n```{C}\n/* load lon-lat coordinates */\nint count = ...;\nfloat *xy_in = ...;\nPLDatumShiftBuffer *buf = pl_load_datum_shift_data(ctx, PL_SPHEROID_WGS_84,\n    xy_in, count, \u0026error);\n\n/* allocate space for result */\nfloat *xy_out = malloc(2 * count * sizeof(float));\n\n/* perform the shift */\nerror = pl_shift_datum(ctx, \n        PL_DATUM_NAD_83,  /* source */\n        PL_DATUM_NAD_27,  /* destination */\n        PL_SPHEROID_CLARKE_1866, /* destination spheroid */\n        buf, xy_out);\n\npl_unload_datum_shift_data(buf);\n```\n\nImage warping\n---\n\n```{C}\n#include \u003cprojcl/projcl.h\u003e\n#include \u003cprojcl/projcl_warp.h\u003e\n\nvoid *src_image_data = ...;\nvoid *dst_image_data = malloc(...);\n\nPLProjectionParams *src_params = pl_params_init();\nPLProjectionParams *dst_params = pl_params_init();\n/* set projection parameters here... */\n\n\n/* load the source image (raw pixel data) */\nPLImageBuffer *image = pl_load_image(pl_ctx,\n    CL_RGBA,            /* channel order */\n    CL_UNSIGNED_INT8,   /* channel type */\n    1024,               /* image width */\n    1024,               /* image height */\n    0,                  /* row pitch (0 to calculate automatically) */\n    src_image_data,\n    1,                  /* copy data? */\n    \u0026error);\n\n/* load a grid of destination points to back-project to the source */\nPLPointGridBuffer *xy_grid = pl_load_grid(ctx,\n    0.0,   /* X-origin */\n    100.0, /* width in projected coordinate space */\n    1024,  /* width in pixels */\n    0.0,   /* Y-origin */\n    100.0, /* height in projected coordinate space */\n    1024,  /* height in pixels */\n    \u0026error);\n\nPLPointGridBuffer *geo_grid = pl_load_empty_grid(pl_ctx, 1024, 1024, \u0026error);\n\n/* project destination XY coordinates back to geo */\npl_project_grid_reverse(pl_ctx, PL_PROJECT_WINKEL_TRIPEL, dst_params, xy_grid, geo_grid);\n\n/* project geo to source XY coordinates */\npl_project_grid_forward(pl_ctx, PL_PROJECT_MERCATOR, src_params, geo_grid, xy_grid);\n\n/* now sample the points using your favorite filter */\npl_sample_image(pl_ctx, xy_grid, image, PL_IMAGE_FILTER_BICUBIC, dst_image_data);\n```\n\nSee\n[include/projcl/projcl_warp.h](https://github.com/evanmiller/ProjCL/blob/master/include/projcl/projcl_warp.h)\nfor a more complete discussion, including how to perform datum conversion on the point grid.\n\nForward geodesic: Fixed distance, multiple points, multiple angles (blast radii)\n--\n\n```{C}\n/* get the point data from somewhere */\nfloat *xy_in = ...;\nint xy_count = ...;\n\n/* get the angle (azimuth) data from somewhere */\nfloat *az_in = ...;\nint az_count = ...;\n\n/* load it up */\nPLForwardGeodesicFixedDistanceBuffer *buf = pl_load_forward_geodesic_fixed_distance_data(ctx,\n    xy_in, xy_count, az_in, az_count, \u0026error);\n\n/* allocate output buffer */\nfloat *xy_out = malloc(2 * xy_count * az_count * sizeof(float));\n\n/* compute */\nerror = pl_forward_geodesic_fixed_distance(ctx, buf, xy_out, PL_SPHEROID_SPHERE,\n        1000.0 /* distance in meters */\n        );\n\n/* unload */\npl_unload_forward_geodesic_fixed_distance_data(buf);\n```\n\nForward geodesic: Fixed angle, single point, multiple distances (great circle)\n--\n\n```{C}\nint count = ...;\nfloat *dist_in = ...;\n\nPLForwardGeodesicFixedAngleBuffer *buf = pl_load_forward_geodesic_fixed_angle_data(ctx,\n    dist_in, count, \u0026error);\n\nfloat *xy_out = malloc(2 * count * sizeof(float));\nfloat xy_in[2] = ...;\n\nerror = pl_forward_geodesic_fixed_angle(ctx, buf, xy_in, xy_out, PL_SPHEROID_SPHERE, \n        M_PI_2 /* angle in radians */\n        );\n\npl_unload_forward_geodesic_fixed_angle_data(buf);\n```\n\nInverse geodesic: Many-to-many (distance table)\n--\n\n```{C}\nint count1 = ...;\nfloat *xy1_in = ...;\n\nint count2 = ...;\nfloat *xy2_in = ...;\n\nfloat *dist_out = malloc(count1 * count2 * sizeof(float));\n\nPLInverseGeodesicBuffer *buf = pl_load_inverse_geodesic_data(ctx, \n        xy1_in, count1, 1, xy2_in, count2, \u0026error);\n\nerror = pl_inverse_geodesic(ctx, buf, dist_out, PL_SPHEROID_SPHERE, \n        1.0 /* scale */);\n\npl_unload_inverse_geodesic_data(buf);\n```\n\nAdding a Map Projection\n--\n\nIt's relatively straightforward to add a map projection to ProjCL. You just\nneed to...\n\n1. Create a file kernel/pl_project_\u0026lt;name\u0026gt;.opencl in kernel/ with the projection routines\n\n2. Create a pl_enqueue_kernel_\u0026lt;name\u0026gt; function in [src/projcl_run.c](https://github.com/evanmiller/ProjCL/blob/master/src/projcl_run.c)\n\n3. Add an entry to the `PLProjection` enum in [include/projcl/projcl_types.h](https://github.com/evanmiller/ProjCL/blob/master/include/projcl/projcl_types.h)\n\n4. Add an entry to the `_pl_projection_info` array in [src/projcl_run.c](https://github.com/evanmiller/ProjCL/blob/master/src/projcl_run.c)\n\n5. Add tests to [test/projcl_test.c](https://github.com/evanmiller/ProjCL/blob/master/test/projcl_test.c)\n\nSome tips on writing OpenCL routines:\n\n* Use float16 arrays of 8 points for input and output\n* Use `any` and `all` for break conditions\n* Use `select` or the ternary operator for conditional assignments\n* Use `sincos` if you need the sine and cosine of the same angle\n* Double-angle and half-angle formulas are your friend\n* `log(tan(M_PI_4 + 0.5*x)) == asinh(tan(x))`\n* `2*atan(exp(x)) - M_PI_2 == asin(tanh(x))`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevanmiller%2Fprojcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevanmiller%2Fprojcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevanmiller%2Fprojcl/lists"}