Parcourir la source

Allow to use several altitude providers

Two altitude providers are now used by default:

- a very accurate provider, but limited to France
- as a fallback, a less accurate provider, but with a global coverage
Baptiste Jonglez il y a 6 ans
Parent
commit
1832e49b1a
4 fichiers modifiés avec 81 ajouts et 19 suppressions
  1. 65 0
      altitude/providers.py
  2. 2 2
      altitude/urls.py
  3. 8 15
      altitude/views.py
  4. 6 2
      celutz/settings.py

+ 65 - 0
altitude/providers.py

@@ -0,0 +1,65 @@
+from __future__ import unicode_literals, division, print_function
+
+import requests
+import logging
+import re
+
+
+logger = logging.getLogger(__name__)
+
+
+class AltitudeProvider(object):
+    url_template = "http://example.com/{lon}/{lat}"
+
+    def parse_answer(self, req):
+        """[req] is a Request instances from requests.  Should return a float."""
+        try:
+            return float(req.text)
+        except ValueError:
+            raise
+
+
+class GeonamesProvider(AltitudeProvider):
+    url_template = "http://api.geonames.org/astergdem?lat={lat}&lng={lon}&username=celutz&style=full"
+
+
+class GeoportailProvider(AltitudeProvider):
+    url_template = "https://wxs.ign.fr/an7nvfzojv5wa96dsga5nk8w/alti/rest/elevation.xml?lon={lon}&lat={lat}&indent=false&crs=%27CRS:84%27&zonly=true"
+
+    def parse_answer(self, req):
+        m = re.search(r'<z>(.*)</z>', req.text)
+        if m == None:
+            raise ValueError
+        return float(m.group(1))
+
+
+# Main function
+def get_altitude(providers, timeout, latitude, longitude):
+    """Given a list of altitude provider classes, and a timeout for each
+    provider, try them all in order until we obtain a reasonable altitude
+    for the given coordinates.
+
+    If all providers fail, returns None.
+    """
+    # Try all providers in order
+    for Provider in providers:
+        name = Provider.__name__
+        logger.info("Trying {}…".format(name))
+        provider = Provider()
+        url = provider.url_template.format(lat=latitude, lon=longitude)
+        try:
+            r = requests.get(url, timeout=timeout)
+        except requests.exceptions.ReadTimeout:
+            logger.warning("{} timeout out after {} seconds".format(name, timeout))
+            continue
+        if r.status_code != 200:
+            continue
+        try:
+            alt = provider.parse_answer(r)
+            logger.info("Got {}m".format(alt))
+            if alt < 0:
+                continue
+            return alt
+        except ValueError:
+            continue
+    # If all providers failed, return nothing

+ 2 - 2
altitude/urls.py

@@ -1,8 +1,8 @@
 from django.conf.urls import url
 from django.views.decorators.cache import cache_page
 
-from altitude.views import geonames_altitude
+from altitude.views import get_altitude
 
 urlpatterns = [
-    url(r'^(?P<lat>-?\d+(?:\.\d+)?)/(?P<lon>-?\d+(?:\.\d+)?)/$', cache_page(7*24*60*60)(geonames_altitude)),
+    url(r'^(?P<lat>-?\d+(?:\.\d+)?)/(?P<lon>-?\d+(?:\.\d+)?)/$', cache_page(7*24*60*60)(get_altitude)),
 ]

+ 8 - 15
altitude/views.py

@@ -1,23 +1,16 @@
-import requests
-import logging
-
 from django.http import HttpResponse, HttpResponseServerError
 from django.conf import settings
 
-logger = logging.getLogger(__name__)
+import altitude.providers
 
 
-def geonames_altitude(request, lat, lon):
+def get_altitude(request, lat, lon):
     lat = float(lat)
     lon = float(lon)
-    url = settings.GEONAMES_ASTERGDEM.format(lat=lat, lon=lon)
-    r = requests.get(url)
-    if r.status_code != 200:
-        return HttpResponseServerError()
-    # The API sometimes returns an error but still sends a 200 code,
-    # so we validate the answer just to make sure...
-    try:
-        return HttpResponse(float(r.text))
-    except ValueError:
-        logger.warning("api.geonames.org error: {}".format(r.text))
+    alt = altitude.providers.get_altitude(settings.ALTITUDE_PROVIDERS,
+                                          settings.ALTITUDE_PROVIDER_TIMEOUT,
+                                          lat, lon)
+    if alt == None:
         return HttpResponseServerError()
+    else:
+        return HttpResponse(alt)

+ 6 - 2
celutz/settings.py

@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
 import os
 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
 
+import altitude.providers
 
 # Quick-start development settings - unsuitable for production
 # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
@@ -119,8 +120,11 @@ STATIC_URL = '/static/'
 # Is it required to login to use celutz?
 LOGIN_REQUIRED = False
 
-
-GEONAMES_ASTERGDEM = "http://api.geonames.org/astergdem?lat={lat}&lng={lon}&username=celutz&style=full"
+# Altitude providers are tried in order until obtaining a result.
+ALTITUDE_PROVIDERS = [altitude.providers.GeoportailProvider,
+                      altitude.providers.GeonamesProvider]
+# Connection timeout for each provider, in seconds
+ALTITUDE_PROVIDER_TIMEOUT = 3.
 
 # For uploaded panorama
 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')