{"id":20766441,"url":"https://github.com/agrc/dhhs-cooling-towers","last_synced_at":"2026-05-27T12:33:03.708Z","repository":{"id":157420797,"uuid":"618204575","full_name":"agrc/dhhs-cooling-towers","owner":"agrc","description":"Tools to extract cooling tower locations from aerial imagery","archived":false,"fork":false,"pushed_at":"2025-12-01T16:02:09.000Z","size":329,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"dev","last_synced_at":"2025-12-19T21:52:40.048Z","etag":null,"topics":["aerial-imagery","artificial-intelligence","computer-vision","cooling-towers","gis","government","government-app","image-processing","machine-learning","utah"],"latest_commit_sha":null,"homepage":"","language":"Python","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/agrc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-03-24T00:59:12.000Z","updated_at":"2025-12-01T16:02:11.000Z","dependencies_parsed_at":"2026-01-19T01:03:25.491Z","dependency_job_id":null,"html_url":"https://github.com/agrc/dhhs-cooling-towers","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/agrc/dhhs-cooling-towers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrc%2Fdhhs-cooling-towers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrc%2Fdhhs-cooling-towers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrc%2Fdhhs-cooling-towers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrc%2Fdhhs-cooling-towers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agrc","download_url":"https://codeload.github.com/agrc/dhhs-cooling-towers/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrc%2Fdhhs-cooling-towers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33566872,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["aerial-imagery","artificial-intelligence","computer-vision","cooling-towers","gis","government","government-app","image-processing","machine-learning","utah"],"created_at":"2024-11-17T11:23:49.952Z","updated_at":"2026-05-27T12:33:03.689Z","avatar_url":"https://github.com/agrc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dhhs-cooling-towers\n\nTools to extract cooling tower locations from aerial imagery\n\n## Prerequisites\n1. Create WMTS index\n   - Run `prerequisites\\polars_build_offet_index.py`\n   - This creates a parquet file (`imagery_index.parquet`) that will be loaded into BigQuery by terraform\n1. Create the processing footprint\n   - This was done manually in ArcGIS Pro with the following steps:\n      1. Buffer [Utah Census Places 2020](https://opendata.gis.utah.gov/datasets/utah-census-places-2020/explore) by 800m\n      1. Query [Utah Buildings](https://opendata.gis.utah.gov/datasets/utah-buildings/explore) down to those larger than 464.5 sq m (5000 sq ft)\n      1. Select by Location the queried buildings that are more than 800m from the census places buffer\n      1. Export selected buildings to a new layer\n      1. Buffer the new buildings layer by 800m\n      1. Combine the buffered census places and buffered buildings into a single polygon layer\n      1. Simplify the combined polygon layer to remove vertices\n      1. Project the simplified polygon layer to WGS84 (EPSG: 4326)\n      1. Export the projected polygon layer to shapefile (processing_footprint.shp)\n      1. Convert the processing footprint from shapefile to CSV with geometries represented as GeoJSON using GDAL\n         - Use the process outlined in this [Blog Post](https://medium.com/google-cloud/how-to-load-geographic-data-like-zipcode-boundaries-into-bigquery-25e4be4391c8) about loading geographic data into BigQuery\n         - `ogr2ogr -f csv -dialect sqlite -sql \"select AsGeoJSON(geometry) AS geom, * from processing_footprint\" footprints_in_4326.csv processing_footprint.shp`\n      1. The `footprints_in_4326.csv` file will be loaded into BigQuery by terraform\n\n\n\n## Data preparation\n\n1. Run the tower scout terraform\n   - this is a private github repository\n1. Execute the two data transfers in order\n1. Execute the two scheduled queries in order\n1. Export `{PROJECT_ID}.indices.images_within_habitat` to GCS\n\n   _there is a terraform item for this but I don't know how it will work since the data transfers are manual and the table may not exist_\n\n   - GCS Location: `{PROJECT_ID}.images_within_habitat.csv`\n   - Export format: `CVS`\n   - Compression: `None`\n\n1. Using the cloud sql proxy\n\n   1. Create a cloud sql table for the task tracking\n\n      ```sql\n      CREATE TABLE public.images_within_habitat (\n         row_num int NULL,\n         col_num int NULL,\n         processed bool NULL DEFAULT false\n      );\n\n      CREATE UNIQUE INDEX idx_images_within_habitat_all ON public.images_within_habitat USING btree (row_num, col_num, processed);\n      ```\n\n      1. Create a cloud sql table for the results\n\n      ```sql\n      CREATE TABLE public.cooling_tower_results (\n         envelope_x_min decimal NULL,\n         envelope_y_min decimal NULL,\n         envelope_x_max decimal NULL,\n         envelope_y_max decimal NULL,\n         confidence decimal NULL,\n         object_class int NULL,\n         object_name varchar NULL,\n         centroid_x_px decimal NULL,\n         centroid_y_px decimal NULL,\n         centroid_x_3857 decimal NULL,\n         centroid_y_3857 decimal NULL\n      );\n      ```\n\n      1. Grant access to users\n\n      ```sql\n         GRANT pg_read_all_data TO \"cloud-run-sa@ut-dts-agrc-dhhs-towers-dev.iam\";\n         GRANT pg_write_all_data TO \"cloud-run-sa@ut-dts-agrc-dhhs-towers-dev.iam\";\n      ```\n\n1. Import the CSV into the `images_within_habitat` table\n\n## To work with the CLI locally\n\n1. Download the PyTorch model weights file and place in the `tower_scout` directory\n   - Add URL\n1. Clone YOLOv5 repository from parent directory\n\n   ```sh\n   git clone https://github.com/ultralytics/yolov5\n   ```\n\n1. Create virtual environment from the parent directory with Python 3.10\n\n   ```sh\n   python -m venv .env\n   .env\\Scripts\\activate.bat\n   pip install -r requirements.dev.txt\n   ```\n\n## CLI\n\nTo work with the CLI,\n\n1. Create a python environment and install the `requirements.dev.txt` into that environment\n1. Execute the CLI to see the commands and options available\n   - `python cool_cli.py`\n\n## Testing\n\n## Cloud Run Job\n\nTo test a small amount of data\n\n1. Set the number of tasks to 1\n1. Set the environment variables\n   - `SKIP`: int e.g. 1106600\n   - `TAKE`: int e.g. 50\n   - `JOB_NAME`: string e.g. alligator\n\nTo run a batch job\n\n1. Set the number of tasks to your desired value e.g. 10000\n1. Set the concurrency to your desired value e.g. 35\n1. Set the environment variables\n   - `JOB_NAME`: string e.g. alligator\n   - `JOB_SIZE`: int e.g. 50 (this value needs to be processable within the timeout)\n\nOur metrics show that we can process 10 jobs a minute. The default cloud run timeout is 10 minutes.\n\n## References for Identifying Cooling Towers in Aerial Imagery\n- [CDC Procedures for Identifying Cooling Towers](https://www.cdc.gov/legionella/health-depts/environmental-inv-resources/id-cooling-towers.html)\n- [CDC Photos of Cooling Towers](https://www.cdc.gov/legionella/health-depts/environmental-inv-resources/cooling-tower-images.html)\n- [California Department of Public Health: Cooling Tower Identification 101](https://www.cdph.ca.gov/Programs/CID/DCDC/CDPH%20Document%20Library/CoolingTowerIDGuideProtocol.pdf)\n- [California Department of Public Health: Cooling Tower Identification Guide Supplement](https://www.cdph.ca.gov/Programs/CID/DCDC/CDPH%20Document%20Library/CTIDGuideSupplement.pdf)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrc%2Fdhhs-cooling-towers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagrc%2Fdhhs-cooling-towers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrc%2Fdhhs-cooling-towers/lists"}