Skip to content

Commit

Permalink
Add upport for elevation checking
Browse files Browse the repository at this point in the history
  • Loading branch information
qba73 committed Dec 11, 2023
1 parent 21d6c55 commit 682b331
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 17 deletions.
117 changes: 111 additions & 6 deletions elevation.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,123 @@
package geonames

type ElevationSRTM1 struct {
import (
"context"
"fmt"
)

type srtm1Resp struct {
Srtm1 int `json:"srtm1"`
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
}

type srtm3Resp struct {
Srtm3 int `json:"srtm3"`
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
}

type asterResp struct {
Astergdem int `json:"astergdem"`
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
}

type gtopoResp struct {
Gtopo30 int `json:"gtopo30"`
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
}

// Elevation holds elevation data expressed in meters npm.
type Elevation struct {
Type string
Lat float64
Lng float64
Type string
Lat float64
Lng float64
Value int
}

// GetElevationSRTM1 takes two float numbers representing latitude and longitude
// and returns elevation in meters according to SRMT1. The sample area is ca 30m x 30m.
// Ocean areas returns "no data", and have assigned a value of -32768.
func (c *Client) GetElevationSRTM1(ctx context.Context, lat, lng float64) (Elevation, error) {
path := fmt.Sprintf("/srtm1JSON?lat=%.3f&lng=%.3f&username=%s", lat, lng, c.UserName)
var er srtm1Resp
err := c.get(ctx, c.BaseURL+path, &er)
if err != nil {
return Elevation{}, err
}
e := Elevation{
Type: "srtm1",
Lat: er.Lat,
Lng: er.Lng,
Value: er.Srtm1,
}
return e, nil
}

func GetElevationSRTM1() Elevation {
// GetElevationSRTM3 takes two float numbers representing latitude and longitude
// and returns elevation in meters according to SRMT3.
//
// SRTM data consisted of a specially modified radar system that flew
// onboard the Space Shuttle Endeavour during an 11-day mission in February of 2000.
// The dataset covers land areas between 60 degrees north and 56 degrees south.
// SRTM3 data are data points located every 3-arc-second (approximately 90 meters) on a latitude/longitude grid.
func (c *Client) GetElevationSRTM3(ctx context.Context, lat, lng float64) (Elevation, error) {
path := fmt.Sprintf("/srtm3JSON?lat=%.3f&lng=%.3f&username=%s", lat, lng, c.UserName)
var er srtm3Resp
err := c.get(ctx, c.BaseURL+path, &er)
if err != nil {
return Elevation{}, err
}
e := Elevation{
Type: "srtm3",
Lat: er.Lat,
Lng: er.Lng,
Value: er.Srtm3,
}
return e, nil
}

// GetElevationAstergdem returns elevation in meters according to aster gdem.
//
// Sample are: ca 30m x 30m, between 83N and 65S latitude. Ocean areas have been assigned a value of -32768
func (c *Client) GetElevationAstergdem(ctx context.Context, lat, lng float64) (Elevation, error) {
path := fmt.Sprintf("/astergdemJSON?lat=%.3f&lng=%.3f&username=%s", lat, lng, c.UserName)
var er asterResp
err := c.get(ctx, c.BaseURL+path, &er)
if err != nil {
return Elevation{}, err
}
e := Elevation{
Type: "astergdem",
Lat: er.Lat,
Lng: er.Lng,
Value: er.Astergdem,
}
return e, nil
}

return Elevation{}
// GetElevationGTOPO30 returns elevation data sampled for the area of 1km x 1km.
//
// Ocean areas have assigned value of -9999 indicating no data for the requested lat and lng.
// GTOPO30 is a global digital elevation model (DEM) with a horizontal grid spacing
// of 30 arc seconds (approximately 1 kilometer).
// GTOPO30 is derived from several raster and vector sources of topographic information.
//
// Documentation: http://eros.usgs.gov/#/Find_Data/Products_and_Data_Available/gtopo30_info
func (c *Client) GetElevationGTOPO30(ctx context.Context, lat, lng float64) (Elevation, error) {
path := fmt.Sprintf("/gtopo30JSON?lat=%.3f&lng=%.3f&username=%s", lat, lng, c.UserName)
var er gtopoResp
err := c.get(ctx, c.BaseURL+path, &er)
if err != nil {
return Elevation{}, err
}
e := Elevation{
Type: "gtopo30",
Lat: er.Lat,
Lng: er.Lng,
Value: er.Gtopo30,
}
return e, nil
}
131 changes: 120 additions & 11 deletions elevation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,157 @@ package geonames_test

import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/qba73/geonames"
)

// ts is a helper func that creates a test server with embedded URI validation.
var testServer = func(reader io.Reader, wantURI string, t *testing.T) *httptest.Server {
// newElevationtestServer creates a test server with embedded URI validation.
func newElevationTestServer(data []byte, wantURI string, t *testing.T) *httptest.Server {
ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
gotReqURI := r.RequestURI
verifyURIs(wantURI, gotReqURI, t)
_, err := io.Copy(rw, reader)
_, err := io.Copy(rw, bytes.NewBuffer(data))
if err != nil {
t.Fatal(err)
}
}))
return ts
}

func TestGetElevationReturnsDataOnValidInput(t *testing.T) {
func TestGetElevationSRTM1ReturnsDataOnValidInput(t *testing.T) {
t.Parallel()

resp := new(bytes.Buffer)
resp.Read([]byte(`23`))
lat, lng := 50.0, 50.0
wantReqURI := fmt.Sprintf("/srtm1JSON?lat=%.3f&lng=%.3f&username=DummyUser", lat, lng)

ts := testServer(&resp)
ts := newElevationTestServer(srtm1, wantReqURI, t)
defer ts.Close()

client, err := geonames.NewClient("DummyUser", geonames.WithBaseURL(ts.URL))
if err != nil {
t.Fatal(err)
}

want := geonames.Elevation{
Type: "srtm1",
Lat: 54.166,
Lng: -6.083,
Value: 375,
}

got, err := client.GetElevationSRTM1(context.Background(), lat, lng)
if err != nil {
t.Fatal(err)
}

if !cmp.Equal(want, got) {
t.Error(cmp.Diff(want, got))
}
}

func TestGetElevationValidInput(t *testing.T) {
func TestGetElevationSRTM3ReturnsDataOnValidInput(t *testing.T) {
t.Parallel()

wantReqURI := "/srtm1JSON?lat=50&lng=50&username=DummyUser"
ts := newTestServer(testFile, wantReqURI, t)
lat, lng := 55.166, -6.088
wantReqURI := fmt.Sprintf("/srtm3JSON?lat=%.3f&lng=%.3f&username=DummyUser", lat, lng)

ts := newElevationTestServer(srtm3, wantReqURI, t)
defer ts.Close()

client, err := geonames.NewClient("DummyUser", geonames.WithBaseURL(ts.URL))
if err != nil {
t.Fatal(err)
}

client.GetElevation()
want := geonames.Elevation{
Type: "srtm3",
Lat: 55.166,
Lng: -6.088,
Value: 263,
}

got, err := client.GetElevationSRTM3(context.Background(), lat, lng)
if err != nil {
t.Fatal(err)
}

if !cmp.Equal(want, got) {
t.Error(cmp.Diff(want, got))
}
}

func TestGetElevationAstergdemReturnsDataOnValidInput(t *testing.T) {
t.Parallel()

lat, lng := 50.01, 10.20
wantReqURI := fmt.Sprintf("/astergdemJSON?lat=%.3f&lng=%.3f&username=DummyUser", lat, lng)

ts := newElevationTestServer(astergdem, wantReqURI, t)
defer ts.Close()

client, err := geonames.NewClient("DummyUser", geonames.WithBaseURL(ts.URL))
if err != nil {
t.Fatal(err)
}

want := geonames.Elevation{
Type: "astergdem",
Lat: 50.010,
Lng: 10.200,
Value: 206,
}

got, err := client.GetElevationAstergdem(context.Background(), lat, lng)
if err != nil {
t.Fatal(err)
}

if !cmp.Equal(want, got) {
t.Error(cmp.Diff(want, got))
}
}

func TestGetElevationGTOPO30ReturnsDataOnValidInput(t *testing.T) {
t.Parallel()

lat, lng := 47.01, 10.2
wantReqURI := fmt.Sprintf("/gtopo30JSON?lat=%.3f&lng=%.3f&username=DummyUser", lat, lng)

ts := newElevationTestServer(gtopo30, wantReqURI, t)
defer ts.Close()

client, err := geonames.NewClient("DummyUser", geonames.WithBaseURL(ts.URL))
if err != nil {
t.Fatal(err)
}

want := geonames.Elevation{
Type: "gtopo30",
Lat: 47.01,
Lng: 10.20,
Value: 2632,
}

got, err := client.GetElevationGTOPO30(context.Background(), lat, lng)
if err != nil {
t.Fatal(err)
}

if !cmp.Equal(want, got) {
t.Error(cmp.Diff(want, got))
}
}

var (
srtm1 = []byte(`{"srtm1":375,"lng":-6.083,"lat":54.166}`)
srtm3 = []byte(`{"srtm3":263,"lng":-6.088,"lat":55.166}`)
astergdem = []byte(`{"lng":10.2,"astergdem":206,"lat":50.01}`)
gtopo30 = []byte(`{"lng":10.2,"gtopo30":2632,"lat":47.01}`)
)

0 comments on commit 682b331

Please sign in to comment.