{"id":25449313,"url":"https://github.com/dpfens/geonames","last_synced_at":"2026-02-24T10:03:12.751Z","repository":{"id":90663100,"uuid":"247205279","full_name":"dpfens/geonames","owner":"dpfens","description":"Process to create a GEONAMES MySQL database and populate the tables  with data from the GEONAMES file dumps","archived":false,"fork":false,"pushed_at":"2022-03-26T06:28:09.000Z","size":25,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-16T08:43:58.000Z","etag":null,"topics":["bash","geography","geonames","mysql"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dpfens.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2020-03-14T03:35:20.000Z","updated_at":"2024-07-17T06:43:54.000Z","dependencies_parsed_at":null,"dependency_job_id":"fe80decc-61d4-465b-862d-b836d8491d93","html_url":"https://github.com/dpfens/geonames","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dpfens/geonames","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpfens%2Fgeonames","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpfens%2Fgeonames/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpfens%2Fgeonames/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpfens%2Fgeonames/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dpfens","download_url":"https://codeload.github.com/dpfens/geonames/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpfens%2Fgeonames/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29779262,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T04:54:30.205Z","status":"ssl_error","status_checked_at":"2026-02-24T04:53:58.628Z","response_time":75,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["bash","geography","geonames","mysql"],"created_at":"2025-02-17T20:27:33.410Z","updated_at":"2026-02-24T10:03:12.736Z","avatar_url":"https://github.com/dpfens.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Geonames\nA set of bash scripts to create/populate a `geonames` MySQL database.  Based heavily upon the [Geonames Forum thread](http://forum.geonames.org/gforum/posts/list/732.page).\n\n## Usage\n\n### Create database\nCreate the `geonames` MySQL database:\n```bash\nbash create.sh \u003croot\u003e \u003cpassword\u003e\n```\n\n### Import data\nFetch data from the Geonames [file dump](http://download.geonames.org/export/dump/) and import it into the `geonames` MySQL database:\n```bash\nbash import.sh \u003croot\u003e \u003cpassword\u003e\n```\n\n### Daily Modifications\nSchedule the `daily_update.sh` script to run every day to process the `modifications-YYYY-MM-DD.txt` file of updates to the `geoNames` table;\n\n## Understanding the data\nThe primary table is the `GeoNames` table.  The main way I filter that list is using the [Feature Codes](http://www.geonames.org/export/codes.html) in the `fclass` and `fcode` columns.\n\nFor example, rows where `fclass='P'` are places (cities, villages, etc.), and rows where `fclass='A'` are administrative/legal places (countries, states, etc.)\n\n## Querying\n\n### Hierarchical Queries\nTo fetch the full hierarchy of geonames, use a `WITH RECURSIVE` MySQL statement, also called [Hierarchical Table Traversal](https://dev.mysql.com/doc/refman/8.0/en/with.html#common-table-expressions-recursive-hierarchy-traversal).\n\n\nTo fetch all of the geonames a place is within, use the following, where the `placeID` is the `geoNameId` of the place you want the hierarchy for:\n```sql\nWITH RECURSIVE location (id, name, type, fclass, level) AS\n(\n  SELECT geoNameId, name, 'None', geoName.fclass, 1\n    FROM geoName\n    WHERE geoNameId=placeID\n  UNION ALL\n  SELECT geoNameId, geoName.name, hierarchy.type, geoName.fclass, level+1\n    FROM geoName\n    INNER JOIN hierarchy\n    \tON hierarchy.parentId=geoName.geonameid\n    INNER JOIN location\n    \tON location.id=hierarchy.childId\n    WHERE hierarchy.type='ADM'\n)\nSELECT * FROM location ORDER by level\n```\n\n\nTo query all geonames that are within a given geoname, use the following, where placeID could be something like `6254928` (Virginia, USA) and will fetch all counties in Virginia\n```sql\nWITH RECURSIVE location (id, name, type, fclass, level) AS\n(\n  SELECT geoNameId, name, 'None', geoName.fclass, 1\n    FROM geoName\n    WHERE geoNameId=placeID\n  UNION ALL\n  SELECT geoNameId, geoName.name, hierarchy.type, geoName.fclass, level+1\n    FROM geoName\n    INNER JOIN hierarchy\n    \tON hierarchy.childId=geoName.geonameid\n    INNER JOIN location\n    \tON location.id=hierarchy.parentId\n    WHERE hierarchy.type='ADM'\n)\nSELECT * FROM location ORDER by level\n```\n\n### Country containing a point\nTo find out what country a given latitude/longitude is within, use the following query:\n\n```sql\nSELECT name FROM geoName\n  INNER JOIN shapesSpatial\n    ON shapesSpatial.geoNameId=geoName.geoNameId\n  WHERE ST_Within(ST_GeomFromText('POINT(37.532054 -77.427336)', 4326), geom)\n```\n\nSpatial indexes are only applied to `ST_within` and `ST_contains` functions, which makes them critical for optimizing queries involving spatial data.\n\n\n### Nearest Neighbors to a point\nTo find the geoname location based on a given latitude/longitude, use the following query, which fetches the nearest 10 administrative places:\n```sql\nSELECT geoName.* FROM geoName\n  INNER JOIN geoNameSpatial\n    ON geoName.geoNameId=geoNameSpatial.geoNameId\n  WHERE geoName.fclass='A'\n  ORDER BY ST_Distance_Sphere(coordinates, ST_SRID(POINT(-97.745363, 30.324014), 4326))\nLIMIT 10\n```\n\nHowever, the above query takes a long time to complete, because `ST_Distance_Sphere` doesn't use an index.  To speed up the query, we need to find a way to filter our query using `ST_Within` or `ST_Contains` so that a spatial index will be used.\n\nWe can create a bounding box around our given coordinates, so the query will only include that are near to our coordinates `ST_Contains(\u003cbounding box\u003e, geoNameSpatial.coordinates)`.\n\n```sql\nSELECT geoName.* FROM geoName\n  INNER JOIN geoNameSpatial\n\t ON geoNameSpatial.geonameid=geoName.geonameid\nWHERE geoName.fclass='A'\n  AND ST_CONTAINS(\n    ST_SRID(st_makeEnvelope ( POINT(-97.562130172913, 30.20954084441), POINT(-97.928595827087, 30.53848715559)), 4326),\n    geoNameSpatial.coordinates\n  )\n  ORDER BY ST_Distance_Sphere(\n      geoNameSpatial.coordinates,\n      ST_SRID(POINT(-97.745363, 30.324014), 4326),\n      4326\n  )\nLIMIT 10\n```\n\nThis reduces our query time from ~13 seconds to 0.0058 seconds (approximately 2200x speed up).\n\nBut, we still have to calculate our bounding box ourselves.  To make things easier, I included a MySQL `boundingBox` function in `functions/boundingBox.sql` so you won't need to re-implement bounding box calculations in your application.\n\nto use `boundingBox`, you will need to provide the `coordinates (POINT)` you want at the center of your bounding box, `distance (DOUBLE)` from the `coordinates` you want the bounding box to extend, and the `sphereRadius (DOUBLE)` of the planet your coordinates were measured on. `distance` and `sphereRadius` can be in any unit, but both must use the same units.  When using `geoNames` data, `sphereRadius` will be the radius of Earth,  3978.8 miles, or 6371 kilometers.\n\nHere is an example of how to use this query, where the `distance` iextends 10 miles from the given coordinates:\n\n```sql\nSELECT geoName.* FROM geoName\n  INNER JOIN geoNameSpatial\n\t ON geoNameSpatial.geonameid=geoName.geonameid\nWHERE geoName.fclass='A'\n  AND ST_CONTAINS(\n    ST_SRID(boundingBox(ST_SRID(POINT(-97.745363, 30.324014), 4326), 10, 3958.8), 4326),\n    geoNameSpatial.coordinates\n  )\n  ORDER BY ST_Distance_Sphere(\n      geoNameSpatial.coordinates,\n      ST_SRID(POINT(-97.745363, 30.324014), 4326),\n      4326\n  )\nLIMIT 10;\n```\n\nThe results of the above query should be identical to those of the previous query.\n\n**NOTE**: These functions are not loaded into your database automatically, and depends directly on the other functions in the `functions` folder.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpfens%2Fgeonames","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdpfens%2Fgeonames","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpfens%2Fgeonames/lists"}