Skip to content

Instantly share code, notes, and snippets.

@orcaman
Created February 9, 2019 13:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orcaman/8de55f14f1c70ef5b0c124cf2fb7d9d1 to your computer and use it in GitHub Desktop.
Save orcaman/8de55f14f1c70ef5b0c124cf2fb7d9d1 to your computer and use it in GitHub Desktop.
Reverse Geocode NOAA station coordinates
package main
import (
"context"
"encoding/csv"
"fmt"
"log"
"os"
"sync"
"time"
"cloud.google.com/go/bigquery"
"google.golang.org/api/iterator"
"googlemaps.github.io/maps"
)
type station struct {
Usaf string
Lat float64
Lon float64
}
func main() {
os.Remove("result.csv")
file, err := os.Create("result.csv")
checkError("Cannot create file", err)
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
ctx := context.Background()
client, err := bigquery.NewClient(ctx, os.Getenv("GCP_PROJECT_ID"))
q := client.Query("SELECT usaf, lat, lon FROM `bigquery-public-data.noaa_gsod.stations` WHERE country='US'")
q.Location = "US"
job, err := q.Run(ctx)
if err != nil {
checkError("", err)
}
status, err := job.Wait(ctx)
if err != nil {
checkError("", err)
}
if err := status.Err(); err != nil {
checkError("", err)
}
it, err := q.Read(ctx)
if err != nil {
checkError("", err)
}
rows := []station{}
for {
var row station
err := it.Next(&row)
if err == iterator.Done {
break
}
if err != nil {
checkError("", err)
}
rows = append(rows, row)
}
mapsClient, err := maps.NewClient(maps.WithAPIKey(os.Getenv("GOOGLE_MAPS_KEY")))
if err != nil {
checkError("", err)
}
results := [][]string{[]string{
"StationNumber",
"Lat",
"Lon",
"Address",
"State",
"City",
"County",
"Zip"}}
rate := time.Second / 10
throttle := time.Tick(rate)
var wg sync.WaitGroup
for _, row := range rows {
<-throttle
wg.Add(1)
go func(row station) {
defer wg.Done()
req := &maps.GeocodingRequest{
LatLng: &maps.LatLng{row.Lat, row.Lon},
}
extractResult, err := mapsClient.Geocode(ctx, req)
if err != nil {
log.Println(err.Error())
return
}
if len(extractResult) == 0 {
log.Println("GeocodingResults is empty")
return
}
geocoding := extractResult[0]
result := []string{
row.Usaf,
fmt.Sprintf("%f", row.Lat),
fmt.Sprintf("%f", row.Lon),
geocoding.FormattedAddress,
compString(geocoding.AddressComponents, "administrative_area_level_1", true),
compString(geocoding.AddressComponents, "locality", false),
compString(geocoding.AddressComponents, "administrative_area_level_2", true),
compString(geocoding.AddressComponents, "postal_code", true),
}
results = append(results, result)
}(row)
}
wg.Wait()
for _, value := range results {
err := writer.Write(value)
checkError("Cannot write to file", err)
}
}
func compString(comps []maps.AddressComponent, typ string, short bool) string {
for _, comp := range comps {
for _, t := range comp.Types {
if t == typ {
var val string
if short {
val = comp.ShortName
} else {
val = comp.LongName
}
return val
}
}
}
return ""
}
func checkError(message string, err error) {
if err != nil {
log.Fatal(message, err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment