Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,30 @@ func (r *Result) getRelation(id int64) *Relation {
}
return relation
}

func (r *Result) getOldNode(id int64) *Node {
node, ok := r.OldNodes[id]
if !ok {
node = &Node{Meta: Meta{ID: id}}
r.OldNodes[id] = node
}
return node
}

func (r *Result) getOldWay(id int64) *Way {
way, ok := r.OldWays[id]
if !ok {
way = &Way{Meta: Meta{ID: id}}
r.OldWays[id] = way
}
return way
}

func (r *Result) getOldRelation(id int64) *Relation {
relation, ok := r.OldRelations[id]
if !ok {
relation = &Relation{Meta: Meta{ID: id}}
r.OldRelations[id] = relation
}
return relation
}
140 changes: 140 additions & 0 deletions json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package overpass

import (
"encoding/json"
"fmt"
"time"
)

type overpassJsonResponse struct {
OSM3S struct {
TimestampOSMBase time.Time `json:"timestamp_osm_base"`
} `json:"osm3s"`
Elements []overpassJsonResponseElement `json:"elements"`
}

type overpassJsonResponseElement struct {
Type ElementType `json:"type"`
ID int64 `json:"id"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
Timestamp *time.Time `json:"timestamp"`
Version int64 `json:"version"`
Changeset int64 `json:"changeset"`
User string `json:"user"`
UID int64 `json:"uid"`
Nodes []int64 `json:"nodes"`
Members []struct {
Type ElementType `json:"type"`
Ref int64 `json:"ref"`
Role string `json:"role"`
} `json:"members"`
Geometry []struct {
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
} `json:"geometry"`
Bounds *struct {
MinLat float64 `json:"minlat"`
MinLon float64 `json:"minlon"`
MaxLat float64 `json:"maxlat"`
MaxLon float64 `json:"maxlon"`
} `json:"bounds"`
Tags map[string]string `json:"tags"`
}

func unmarshalJson(body []byte) (Result, error) {
var overpassRes overpassJsonResponse
if err := json.Unmarshal(body, &overpassRes); err != nil {
return Result{}, fmt.Errorf("overpass engine error: %w", err)
}

result := Result{
Timestamp: overpassRes.OSM3S.TimestampOSMBase,
Count: len(overpassRes.Elements),
Nodes: make(map[int64]*Node),
Ways: make(map[int64]*Way),
Relations: make(map[int64]*Relation),
}

for _, el := range overpassRes.Elements {
meta := Meta{
ID: el.ID,
Timestamp: el.Timestamp,
Version: el.Version,
Changeset: el.Changeset,
User: el.User,
UID: el.UID,
Tags: el.Tags,
}
switch el.Type {
case ElementTypeNode:
node := result.getNode(el.ID)
*node = Node{
Meta: meta,
Lat: el.Lat,
Lon: el.Lon,
}
case ElementTypeWay:
way := result.getWay(el.ID)
*way = Way{
Meta: meta,
Nodes: make([]*Node, len(el.Nodes)),
Geometry: make([]Point, len(el.Geometry)),
}
for idx, nodeID := range el.Nodes {
way.Nodes[idx] = result.getNode(nodeID)
}
if el.Bounds != nil {
way.Bounds = &Box{
Min: Point{
Lat: el.Bounds.MinLat,
Lon: el.Bounds.MinLon,
},
Max: Point{
Lat: el.Bounds.MaxLat,
Lon: el.Bounds.MaxLon,
},
}
}
for idx, geo := range el.Geometry {
way.Geometry[idx].Lat = geo.Lat
way.Geometry[idx].Lon = geo.Lon
}
case ElementTypeRelation:
relation := result.getRelation(el.ID)
*relation = Relation{
Meta: meta,
Members: make([]RelationMember, len(el.Members)),
}
for idx, member := range el.Members {
relationMember := RelationMember{
Type: member.Type,
Role: member.Role,
}
switch member.Type {
case ElementTypeNode:
relationMember.Node = result.getNode(member.Ref)
case ElementTypeWay:
relationMember.Way = result.getWay(member.Ref)
case ElementTypeRelation:
relationMember.Relation = result.getRelation(member.Ref)
}
relation.Members[idx] = relationMember
}
if el.Bounds != nil {
relation.Bounds = &Box{
Min: Point{
Lat: el.Bounds.MinLat,
Lon: el.Bounds.MinLon,
},
Max: Point{
Lat: el.Bounds.MaxLat,
Lon: el.Bounds.MaxLon,
},
}
}
}
}

return result, nil
}
145 changes: 17 additions & 128 deletions query.go
Original file line number Diff line number Diff line change
@@ -1,48 +1,21 @@
package overpass

import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"regexp"
"time"
)

const ApiOutputFormatJson string = "json"

type overpassResponse struct {
OSM3S struct {
TimestampOSMBase time.Time `json:"timestamp_osm_base"`
} `json:"osm3s"`
Elements []overpassResponseElement `json:"elements"`
}

type overpassResponseElement struct {
Type ElementType `json:"type"`
ID int64 `json:"id"`
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
Timestamp *time.Time `json:"timestamp"`
Version int64 `json:"version"`
Changeset int64 `json:"changeset"`
User string `json:"user"`
UID int64 `json:"uid"`
Nodes []int64 `json:"nodes"`
Members []struct {
Type ElementType `json:"type"`
Ref int64 `json:"ref"`
Role string `json:"role"`
} `json:"members"`
Geometry []struct {
Lat float64 `json:"lat"`
Lon float64 `json:"lon"`
} `json:"geometry"`
Bounds *struct {
MinLat float64 `json:"minlat"`
MinLon float64 `json:"minlon"`
MaxLat float64 `json:"maxlat"`
MaxLon float64 `json:"maxlon"`
} `json:"bounds"`
Tags map[string]string `json:"tags"`
Elements []overpassJsonResponse `json:"elements"`
}

// Query send request to OverpassAPI with provided querystring.
Expand All @@ -52,104 +25,11 @@ func (c *Client) Query(query string) (Result, error) {
return Result{}, err
}

return unmarshal(body)
}

func unmarshal(body []byte) (Result, error) {
var overpassRes overpassResponse
if err := json.Unmarshal(body, &overpassRes); err != nil {
return Result{}, fmt.Errorf("overpass engine error: %w", err)
}

result := Result{
Timestamp: overpassRes.OSM3S.TimestampOSMBase,
Count: len(overpassRes.Elements),
Nodes: make(map[int64]*Node),
Ways: make(map[int64]*Way),
Relations: make(map[int64]*Relation),
outF := getOutputFormatFromQuery(query)
if outF == ApiOutputFormatJson {
return unmarshalJson(body)
}

for _, el := range overpassRes.Elements {
meta := Meta{
ID: el.ID,
Timestamp: el.Timestamp,
Version: el.Version,
Changeset: el.Changeset,
User: el.User,
UID: el.UID,
Tags: el.Tags,
}
switch el.Type {
case ElementTypeNode:
node := result.getNode(el.ID)
*node = Node{
Meta: meta,
Lat: el.Lat,
Lon: el.Lon,
}
case ElementTypeWay:
way := result.getWay(el.ID)
*way = Way{
Meta: meta,
Nodes: make([]*Node, len(el.Nodes)),
Geometry: make([]Point, len(el.Geometry)),
}
for idx, nodeID := range el.Nodes {
way.Nodes[idx] = result.getNode(nodeID)
}
if el.Bounds != nil {
way.Bounds = &Box{
Min: Point{
Lat: el.Bounds.MinLat,
Lon: el.Bounds.MinLon,
},
Max: Point{
Lat: el.Bounds.MaxLat,
Lon: el.Bounds.MaxLon,
},
}
}
for idx, geo := range el.Geometry {
way.Geometry[idx].Lat = geo.Lat
way.Geometry[idx].Lon = geo.Lon
}
case ElementTypeRelation:
relation := result.getRelation(el.ID)
*relation = Relation{
Meta: meta,
Members: make([]RelationMember, len(el.Members)),
}
for idx, member := range el.Members {
relationMember := RelationMember{
Type: member.Type,
Role: member.Role,
}
switch member.Type {
case ElementTypeNode:
relationMember.Node = result.getNode(member.Ref)
case ElementTypeWay:
relationMember.Way = result.getWay(member.Ref)
case ElementTypeRelation:
relationMember.Relation = result.getRelation(member.Ref)
}
relation.Members[idx] = relationMember
}
if el.Bounds != nil {
relation.Bounds = &Box{
Min: Point{
Lat: el.Bounds.MinLat,
Lon: el.Bounds.MinLon,
},
Max: Point{
Lat: el.Bounds.MaxLat,
Lon: el.Bounds.MaxLon,
},
}
}
}
}

return result, nil
return unmarshalXml(body)
}

func (c *Client) httpPost(query string) ([]byte, error) {
Expand Down Expand Up @@ -190,3 +70,12 @@ type ServerError struct {
func (e *ServerError) Error() string {
return fmt.Sprintf("%d %s", e.StatusCode, http.StatusText(e.StatusCode))
}

func getOutputFormatFromQuery(query string) string {
re := regexp.MustCompile("\\[out:([a-z]+)]")
match := re.FindStringSubmatch(query)
if len(match) == 2 {
return match[1]
}
return ""
}
Loading