nominatim_search.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Copyright (C) 2014 Daniel 'grindhold' Brendle
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published
  6. * by the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * Authors:
  18. * Daniel 'grindhold' Brendle <grindhold@skarphed.org>
  19. */
  20. package gominatim
  21. import (
  22. "encoding/json"
  23. "errors"
  24. "io/ioutil"
  25. "net/http"
  26. "net/url"
  27. "strconv"
  28. )
  29. type searchResultError struct {
  30. error string `json:"error"`
  31. }
  32. type SearchResult struct {
  33. PlaceId string `json:"place_id"`
  34. License string `json:"license"`
  35. OsmType string `json:"osm_type"`
  36. OsmId string `json:"osm_id"`
  37. Boundingbox []string `json:"boundingbox"`
  38. Polygonpoints [][]string `json:"polygonpoints"`
  39. Lat string `json:"lat"`
  40. Lon string `json:"lon"`
  41. DisplayName string `json:"display_name"`
  42. Class string `json:"class"`
  43. Type string `json:"type"`
  44. Address Address `json:"address"`
  45. Importance float32 `json:"importance""`
  46. }
  47. type SearchQuery struct {
  48. JsonCallback interface{}
  49. AcceptLanguage string
  50. Q string
  51. Street string
  52. City string
  53. County string
  54. State string
  55. Postalcode string
  56. Countrycodes []string
  57. Viewbox string
  58. Bounded bool
  59. Polygon bool
  60. Addressdetails bool
  61. Email string
  62. ExcludePlaceIds []string
  63. Limit int
  64. PolygonGeojson bool
  65. PolygonKml bool
  66. PolygonText bool
  67. PolygonSvg bool
  68. }
  69. func (q *SearchQuery) specificFieldsUsed() bool {
  70. return q.Street != "" || q.City != "" || q.County != "" || q.State != "" || q.Postalcode != ""
  71. }
  72. func (q *SearchQuery) buildQuery() (string, error) {
  73. if server == "" {
  74. return "", errors.New("Server is not set. Set via gominatim.SetServer(srv string)")
  75. }
  76. s := server
  77. s = s + "/search?format=json"
  78. if q.JsonCallback != nil {
  79. cb, err := json.Marshal(q.JsonCallback)
  80. if err != nil {
  81. return "", err
  82. }
  83. s += "&json_callback=" + string(cb)
  84. }
  85. if q.AcceptLanguage != "" {
  86. s += "&accept-language=" + url.QueryEscape(q.AcceptLanguage)
  87. }
  88. if q.Q != "" {
  89. s += "&q=" + url.QueryEscape(q.Q)
  90. } else {
  91. if q.specificFieldsUsed() {
  92. if q.Street != "" {
  93. s += "&street=" + url.QueryEscape(q.Street)
  94. }
  95. if q.City != "" {
  96. s += "&city=" + url.QueryEscape(q.City)
  97. }
  98. if q.County != "" {
  99. s += "&county=" + url.QueryEscape(q.County)
  100. }
  101. if q.State != "" {
  102. s += "&state=" + url.QueryEscape(q.State)
  103. }
  104. if q.Postalcode != "" {
  105. s += "&postalcode=" + url.QueryEscape(q.Postalcode)
  106. }
  107. } else {
  108. return "", errors.New("You must use either Q or one or more of Street, City, County, State, Postalcode. The latter will be ignored if the further is used.")
  109. }
  110. }
  111. if q.Countrycodes != nil && len(q.Countrycodes) > 0 {
  112. als := ""
  113. first := true
  114. for i := range q.Countrycodes {
  115. if !first {
  116. als = als + ","
  117. }
  118. als = als + q.Countrycodes[i]
  119. if first {
  120. first = false
  121. }
  122. }
  123. s += "&countrycodes=" + url.QueryEscape(als)
  124. }
  125. if q.Viewbox != "" {
  126. s += "&viewbox=" + url.QueryEscape(q.Viewbox)
  127. }
  128. if q.Bounded {
  129. s += "&bounded=1"
  130. } else {
  131. s += "&bounded=0"
  132. }
  133. if q.Polygon {
  134. s += "&polygon=1"
  135. } else {
  136. s += "&polygon=0"
  137. }
  138. if q.Addressdetails {
  139. s += "&addressdetails=1"
  140. } else {
  141. s += "&addressdetails=0"
  142. }
  143. if q.Email != "" {
  144. s += "&email=" + url.QueryEscape(q.Email)
  145. }
  146. if q.ExcludePlaceIds != nil && len(q.ExcludePlaceIds) > 0 {
  147. als := ""
  148. first := true
  149. for i := range q.ExcludePlaceIds {
  150. if !first {
  151. als = als + ","
  152. }
  153. als = als + q.ExcludePlaceIds[i]
  154. if first {
  155. first = false
  156. }
  157. }
  158. s += "&exclude_place_ids=" + url.QueryEscape(als)
  159. }
  160. if q.Limit > 0 {
  161. s += "&limit=" + strconv.Itoa(q.Limit)
  162. }
  163. if q.PolygonGeojson {
  164. s += "&polygon_geojson=1"
  165. } else {
  166. s += "&polygon_geojson=0"
  167. }
  168. if q.PolygonKml {
  169. s += "&polygon_kml=1"
  170. } else {
  171. s += "&polygon_kml=0"
  172. }
  173. if q.PolygonSvg {
  174. s += "&polygon_svg=1"
  175. } else {
  176. s += "&polygon_svg=0"
  177. }
  178. if q.PolygonText {
  179. s += "&polygon_text=1"
  180. } else {
  181. s += "&polygon_text=0"
  182. }
  183. return s, nil
  184. }
  185. func (q *SearchQuery) Get() ([]SearchResult, error) {
  186. querystring, err := q.buildQuery()
  187. if err != nil {
  188. return nil, err
  189. }
  190. resp, err := http.Get(querystring)
  191. if err != nil {
  192. return nil, err
  193. }
  194. defer resp.Body.Close()
  195. body, err := ioutil.ReadAll(resp.Body)
  196. if err != nil {
  197. return nil, err
  198. }
  199. result := make([]SearchResult, 0)
  200. err = json.Unmarshal(body, &result)
  201. if err != nil {
  202. err_obj := new(searchResultError)
  203. err := json.Unmarshal(body, err_obj)
  204. if err != nil {
  205. return nil, err
  206. } else {
  207. return nil, errors.New(err_obj.error)
  208. }
  209. }
  210. if len(result) == 0 {
  211. return nil, errors.New("Nothing found; sorry :/")
  212. }
  213. return result, nil
  214. }