diff --git a/README.md b/README.md
index 281679ab22e9e11ad18cad8fd21a965483887127..bb776a15ee4ba9192f72873367cec05852b547b6 100644
--- a/README.md
+++ b/README.md
@@ -12,13 +12,13 @@ be harvested, e.g. for inclusion into the TOAR database:
 
 **Valid `SERVICE_NAME`s are:**
 
-`major-roads`: returns distance to major roads using the OpenStreetMap Overpass 
+`major_roads`: returns distance to major roads using the OpenStreetMap Overpass 
     interface
 
-`population-density`: returns aggregate statistics of population density from
+`population_density`: returns aggregate statistics of population density from
     the GPW3.1 1 km dataset
     
-`topography-tandem-x`: returns aggregate statistics of topography from TanDEM-X 
+`topography_tandem_x`: returns aggregate statistics of topography from TanDEM-X 
     90m dataset (provided by German Aerospace Center DLR)
     
 **Arguments:**
@@ -39,17 +39,15 @@ _Note:_ You can also write `long` or `longitude` instead of `lon`, or
     all services. For example, major-raods will always return the nearest 
     distance to a road. `NN-percentile` can also be written as `NN%-ile`.
     
-`direction`=N|NEN|NE|ENE|E|ESE|SE|SES|S|SWS|SW|WSW|W|WNW|NW|NWN: restrict
-    search and/or aggregation to specified wind sector.
-    
-`by_direction`=True: return result for each wind sector. The result will be an
-    array (or dict??) instead of a single value.
+`direction`=N|NEN|NE|ENE|E|ESE|SE|SES|S|SWS|SW|WSW|W|WNW|NW|NWN|all|true: restrict
+    search and/or aggregation to specified wind sector. If set to all or true, return 
+    result for each wind sector
     
 Individual services may have additional optional URL parameters.
 
 ## 2. Individual services
 
-### 2.1 major-roads
+### 2.1 major_roads
 
 This services performs a query to the Overpass Web API of Open Street Map
 and returns the distance 
@@ -60,7 +58,7 @@ and highway name if available.
 
 **OPTIONS:**
 
-The `radius`, `direction`, and `by_direction` arguments work as described above.
+The `radius` and `direction` arguments work as described above.
 The `agg` option is ignored.
 
 `highway_types`=string: specify the Open Street Map highway types to be included
@@ -68,12 +66,12 @@ in the search. Default is to ask for
 _'motorway,trunk,primary,secondary,tertiary'_. Highway types must be specified as 
 comma-separated string. 
 
-The *major-roads* service also has a `map` variant where 
+The *major_roads* service also has a `map` variant where 
 OpenStreetmap tiles are displayed with an overlay of the search point
 and the roads surrounding it.  The url for this is 
-[base-url]/major-roads/map/[?Options].
+[base-url]/major_roads/map/[?Options].
 
-### 2.2 population-density
+### 2.2 population_density
 
 This services queries a local copy of the Global Population of the World, 
 version 3.1 dataset (see [SEDAC](https://doi.org/10.7927/H4XK8CG2)) and
@@ -85,12 +83,12 @@ the `lat` and `lon` arguments or aggregated over a given `radius`.
 All arguments can be used as described above. There are no service-specific 
 arguments.
 
-### 2.3 topography-tandem-x
+### 2.3 topography_tandem_x
 
-This services queries a local copy of TANDEM-X radar topography data from the DLR, Germany.
+This services queries a local copy of TANDEM-X radar topography data from the [DLR, Germany](ftpes://tandemx-90m.dlr.de).
 The dataset has 90 m resolution. The current local copy only covers the region of
 Harz, Germany.
-The service returns the topgraphic altitude at the point location specified by
+The service returns the topographic altitude at the point location specified by
 the `lat` and `lon` arguments or aggregated over a given `radius`.
 
 **OPTIONS:**
@@ -98,18 +96,6 @@ the `lat` and `lon` arguments or aggregated over a given `radius`.
 All arguments can be used as described above. There are no service-specific 
 arguments.
 
-### 2.3 topography-tandem-x
-
-This service works on a local file with topography data previously downloaded from 
-[DLR ftp-server](ftpes://tandemx-90m.dlr.de). It returns the height of topography at
-the point location specified by the `lat` and `lon` arguments or aggregrated over 
-given `radius`.
-
-**OPTIONS:**
-
-All arguments can be used as described above. There are no service-specific 
-arguments.
-
 ## 3. Installation
 
 ### ubuntu
diff --git a/climatic_zones/apps.py b/climatic_zones/apps.py
index a9fa3e119d1d9c05c5d92fb4ec82268c196dd494..6eec2469900a264872fe298f2e0c8a6bcf3a45a7 100644
--- a/climatic_zones/apps.py
+++ b/climatic_zones/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class ClimaticZonesConfig(AppConfig):
+class ClimaticZonesConfig(AppConfig):  # pragma: no cover
     name = 'climatic_zones'
diff --git a/climatic_zones/views.py b/climatic_zones/views.py
index dcee06403e7178537af0c58866f5d70d2c70d39f..72096c844010f47fa66b1e26ce0d3d370a67fe3e 100644
--- a/climatic_zones/views.py
+++ b/climatic_zones/views.py
@@ -31,4 +31,5 @@ class ClimateView(APIView, CommonViews):
         CommonViews.__init__(self, **opts)
         self.keep_only_given_agg_allowed(dict(maxclass=most_common_value,
                                               frequency=relative_frequency),
-                                         frequency=dict(bins=13))
+                                         frequency=dict(bins=13,
+                                                        missing_value=self.default_value))
diff --git a/htap_regions_tier1/apps.py b/htap_regions_tier1/apps.py
index 8dd55a16b6e32c56e37e5fa250b0da2a42dc04dc..8409d408982fe72fc9fa08cc053ca17c6f8c8653 100644
--- a/htap_regions_tier1/apps.py
+++ b/htap_regions_tier1/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class HtapRegionsConfig(AppConfig):
+class HtapRegionsConfig(AppConfig):  # pragma: no cover
     name = 'htap_regions_tier1'
diff --git a/htap_regions_tier1/views.py b/htap_regions_tier1/views.py
index 27cb0ab6c68c5cf4b59e6cb0db2ee17f99ff2947..897b0014eb90c5b912f9b281b1e84c2205c26926 100755
--- a/htap_regions_tier1/views.py
+++ b/htap_regions_tier1/views.py
@@ -34,4 +34,5 @@ class HTAPView(APIView, CommonViews):
         self.keep_only_given_agg_allowed(dict(maxclass=most_common_value,
                                               frequency=relative_frequency),
                                          frequency=dict(bins=len(self.bins),
-                                                        start_bin=self.min_valid))
+                                                        start_bin=self.min_valid,
+                                                        missing_value=self.default_value))
diff --git a/htap_regions_tier2/apps.py b/htap_regions_tier2/apps.py
index d90ec102dcc566593b2e0f29f2ae9b9ecabb7058..7a4760b8d1baf8cf0309bb90058455e5d72d0846 100644
--- a/htap_regions_tier2/apps.py
+++ b/htap_regions_tier2/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class HtapRegionsConfig(AppConfig):
+class HtapRegionsConfig(AppConfig):  # pragma: no cover
     name = 'htap_regions_tier2'
diff --git a/major-roads/apps.py b/major-roads/apps.py
deleted file mode 100644
index 8d6f0c32820f09fb15dc97c5b1a412bc1da93e01..0000000000000000000000000000000000000000
--- a/major-roads/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class LocalConfig(AppConfig):
-    name = 'major-roads'
diff --git a/major-roads/amir_code/README.md b/major_roads/amir_code/README.md
similarity index 100%
rename from major-roads/amir_code/README.md
rename to major_roads/amir_code/README.md
diff --git a/major-roads/amir_code/overpassQL.py b/major_roads/amir_code/overpassQL.py
similarity index 100%
rename from major-roads/amir_code/overpassQL.py
rename to major_roads/amir_code/overpassQL.py
diff --git a/major-roads/amir_code/processor.py b/major_roads/amir_code/processor.py
similarity index 100%
rename from major-roads/amir_code/processor.py
rename to major_roads/amir_code/processor.py
diff --git a/major-roads/amir_code/views.py b/major_roads/amir_code/views.py
similarity index 97%
rename from major-roads/amir_code/views.py
rename to major_roads/amir_code/views.py
index 67a367fa1dc6833c15aa021da31b6af5b59027e0..33baa62e1dc03b809433dc13d6a7d78c6caebc67 100644
--- a/major-roads/amir_code/views.py
+++ b/major_roads/amir_code/views.py
@@ -23,7 +23,7 @@ class Road(APIView):
         example : https://www.openstreetmap.org/way/493995603
         
         USE EXAMPLE:
-        /major-roads/roads-radius/?lat=50.9&lng=6.9&radius=700&direction=ESE
+        /major_roads/roads-radius/?lat=50.9&lng=6.9&radius=700&direction=ESE
 
         PARAMETERS:
         lat
diff --git a/major_roads/apps.py b/major_roads/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..0514d857e22b549fa6f73b6dab383c2c9cb15992
--- /dev/null
+++ b/major_roads/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class LocalConfig(AppConfig):  # pragma: no cover
+    name = 'major_roads'
diff --git a/major-roads/overpass_api.py b/major_roads/overpass_api.py
similarity index 100%
rename from major-roads/overpass_api.py
rename to major_roads/overpass_api.py
diff --git a/major-roads/pygeometry_nearestOn4_tests.py b/major_roads/pygeometry_nearestOn4_tests.py
similarity index 100%
rename from major-roads/pygeometry_nearestOn4_tests.py
rename to major_roads/pygeometry_nearestOn4_tests.py
diff --git a/major-roads/serializers.py b/major_roads/serializers.py
similarity index 100%
rename from major-roads/serializers.py
rename to major_roads/serializers.py
diff --git a/major-roads/static/js/osm_map.js b/major_roads/static/js/osm_map.js
similarity index 100%
rename from major-roads/static/js/osm_map.js
rename to major_roads/static/js/osm_map.js
diff --git a/major-roads/templates/map_view.html b/major_roads/templates/map_view.html
similarity index 100%
rename from major-roads/templates/map_view.html
rename to major_roads/templates/map_view.html
diff --git a/major-roads/tests.py b/major_roads/tests.py
similarity index 100%
rename from major-roads/tests.py
rename to major_roads/tests.py
diff --git a/major-roads/urls.py b/major_roads/urls.py
similarity index 100%
rename from major-roads/urls.py
rename to major_roads/urls.py
diff --git a/major-roads/vector_test.py b/major_roads/vector_test.py
similarity index 100%
rename from major-roads/vector_test.py
rename to major_roads/vector_test.py
diff --git a/major-roads/views.py b/major_roads/views.py
similarity index 94%
rename from major-roads/views.py
rename to major_roads/views.py
index d6794fd9ef5e9e48988d0dbcb1c7ecc843a46972..f4ebceed2b23a03d3591ddedc3f26672dccec8e1 100644
--- a/major-roads/views.py
+++ b/major_roads/views.py
@@ -14,10 +14,10 @@ from .serializers import NearestSerializer, NearestDirectionSerializer, NearestB
 class MajorRoadsView(APIView, CommonViews):
 
     def __init__(self):
-        CommonViews.__init__(self, service_type="major-roads")
+        CommonViews.__init__(self, service_type="major_roads")
 
     def get(self, request, format=None):
-        """process GET requests for major-roads app
+        """process GET requests for major_roads app
 
         returns a Geo-JSON response with information about the nearest major road
 
@@ -33,7 +33,7 @@ class MajorRoadsView(APIView, CommonViews):
         by_direction: if true, then results are returned as array
             (or dict??) for all wind directions.
 
-        optional arguments specific to major-roads service:
+        optional arguments specific to major_roads service:
             highway_types: string with road types according to Overpass syntax.
                 Default is motorway,trunk,primary,secondary.
                 For additional options see https://wiki.openstreetmap.org/wiki/Key:highway
@@ -78,13 +78,13 @@ class MajorRoadsView(APIView, CommonViews):
 class MajorRoadsMapView(APIView, CommonViews):
 
     def __init__(self):
-        CommonViews.__init__(self, service_type="major-roads")
+        CommonViews.__init__(self, service_type="major_roads")
 
     renderer_classes = [TemplateHTMLRenderer]
     template_name = 'map_view.html'
 
     def get(self, request, format=None):
-        """process GET requests for major-roads app
+        """process GET requests for major_roads app
         Works similar to MajorRoadsView but display a template HTML view with a map
         """
         lat, lon, radius, direction, by_direction, highway_types = self.get_query_params(
diff --git a/nox_emissions/apps.py b/nox_emissions/apps.py
index d2ead1f0e40e312b2b75c0cbc517bbf7d1ea167f..88f3ce90702f67e259bdcb22f041be6bb57747d0 100644
--- a/nox_emissions/apps.py
+++ b/nox_emissions/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class NoxEmissionConfig(AppConfig):
+class NoxEmissionConfig(AppConfig):  # pragma: no cover
     name = 'nox_emissions'
diff --git a/omi_no2/apps.py b/omi_no2/apps.py
index 6779f6eb93c1f13d07fb7e135d58311d18edfa03..fd07c8b0958483f8948686ec811d719239ba94b6 100644
--- a/omi_no2/apps.py
+++ b/omi_no2/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class OmiNo2Config(AppConfig):
+class OmiNo2Config(AppConfig):  # pragma: no cover
     name = 'omi_no2'
diff --git a/population-density/apps.py b/population-density/apps.py
deleted file mode 100644
index 58582de03d9e916a668ad756a9d966df2a10a1df..0000000000000000000000000000000000000000
--- a/population-density/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class PopulationConfig(AppConfig):
-    name = 'population-density'
diff --git a/population-density/__init__.py b/population_density/__init__.py
similarity index 100%
rename from population-density/__init__.py
rename to population_density/__init__.py
diff --git a/population-density/admin.py b/population_density/admin.py
similarity index 100%
rename from population-density/admin.py
rename to population_density/admin.py
diff --git a/population_density/apps.py b/population_density/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..f2906a1ff7e5c4380882bcdab1a09b732fc32953
--- /dev/null
+++ b/population_density/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class PopulationConfig(AppConfig):  # pragma: no cover
+    name = 'population_density'
diff --git a/population-density/migrations/__init__.py b/population_density/migrations/__init__.py
similarity index 100%
rename from population-density/migrations/__init__.py
rename to population_density/migrations/__init__.py
diff --git a/population-density/population_file_extraction.py b/population_density/population_file_extraction.py
similarity index 100%
rename from population-density/population_file_extraction.py
rename to population_density/population_file_extraction.py
diff --git a/population-density/serializers.py b/population_density/serializers.py
similarity index 100%
rename from population-density/serializers.py
rename to population_density/serializers.py
diff --git a/population-density/urls.py b/population_density/urls.py
similarity index 100%
rename from population-density/urls.py
rename to population_density/urls.py
diff --git a/population-density/views.py b/population_density/views.py
similarity index 100%
rename from population-density/views.py
rename to population_density/views.py
diff --git a/rice_production/apps.py b/rice_production/apps.py
index a37fc3b5b2d12c854bd13a94a0992f6eb2dfbd0a..4b90c30e8f453e1ca766616f94a1c3fbaf8a0fa4 100644
--- a/rice_production/apps.py
+++ b/rice_production/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class RiceProductionConfig(AppConfig):
+class RiceProductionConfig(AppConfig):  # pragma: no cover
     name = 'rice_production'
diff --git a/stable_night_lights/apps.py b/stable_night_lights/apps.py
index 6cf8d9c7dd27218faeb871686c676ab1aee94b31..c9c0c72077d416a3cc312d7aa27975bf4f83e4f4 100644
--- a/stable_night_lights/apps.py
+++ b/stable_night_lights/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class StableNightLightsConfig(AppConfig):
+class StableNightLightsConfig(AppConfig):  # pragma: no cover
     name = 'stable_night_lights'
diff --git a/toar_location_services/settings.py b/toar_location_services/settings.py
index 2934a79fb99f967650d8ef685ef759eedf30e35c..8cdd5b18a2c7a04dd6318d7bc1f54486d83f9b47 100644
--- a/toar_location_services/settings.py
+++ b/toar_location_services/settings.py
@@ -41,9 +41,9 @@ INSTALLED_APPS = [
     'django.contrib.staticfiles',
     'django.contrib.gis',
     'rest_framework',
-    'major-roads',
-    'population-density',
-    'topography-tandem-x',
+    'major_roads',
+    'population_density',
+    'topography_tandem_x',
     'stable_night_lights',
     'wheat_production',
     'rice_production',
diff --git a/toar_location_services/urls.py b/toar_location_services/urls.py
index 2d9f40bc265727ab6e8ef4cf11ea46544cb740f3..5383997dcc238cfc38f54c61f90de2c0e56d235e 100644
--- a/toar_location_services/urls.py
+++ b/toar_location_services/urls.py
@@ -6,9 +6,9 @@ from toar_location_services.settings import DEBUG
 urlpatterns = [
     url(r'^$', LocationServicesRootView.as_view()),
     url('admin/', admin.site.urls),
-    url(r'major-roads/', include('major-roads.urls'), name='major-roads'),
-    url(r'population-density/', include('population-density.urls'), name='population-density-density'),
-    url(r'topography-tandem-x/', include('topography-tandem-x.urls'), name='topography-tandem-x'),
+    url(r'major_roads/', include('major_roads.urls'), name='major_roads'),
+    url(r'population_density/', include('population_density.urls'), name='population_density'),
+    url(r'topography_tandem_x/', include('topography_tandem_x.urls'), name='topography_tandem_x'),
     url(r'stable_night_lights/', include('stable_night_lights.urls'), name='stable_night_lights'),
     url(r'wheat_production/', include('wheat_production.urls'), name='wheat_production'),
     url(r'rice_production/', include('rice_production.urls'), name='rice_production'),
diff --git a/toar_location_services/views.py b/toar_location_services/views.py
index c0caba9dd754a2f41f76747c08a9a1b7c83e5e3f..d3961505e378fb290876415351c441f6f6834829 100644
--- a/toar_location_services/views.py
+++ b/toar_location_services/views.py
@@ -11,9 +11,9 @@ class LocationServicesRootView(APIView):
         data = [
             'User interface APIs:',
             OrderedDict([
-                ('major-roads', request.build_absolute_uri()+'major-roads/'),
-                ('population-density', request.build_absolute_uri()+'population-density/'),
-                ('topography-tandem-x', request.build_absolute_uri()+'topography-tandem-x/'),
+                ('major_roads', request.build_absolute_uri()+'major_roads/'),
+                ('population_density', request.build_absolute_uri()+'population_density/'),
+                ('topography_tandem_x', request.build_absolute_uri()+'topography_tandem_x/'),
                 ('stable_night_lights', request.build_absolute_uri()+'stable_night_lights/'),
                 ('wheat_production', request.build_absolute_uri()+'wheat_production/'),
                 ('rice_production', request.build_absolute_uri()+'rice_production/'),
diff --git a/topography-tandem-x/apps.py b/topography-tandem-x/apps.py
deleted file mode 100644
index a29302faf62bcc3bdf11fd63294db630fbac6dde..0000000000000000000000000000000000000000
--- a/topography-tandem-x/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class TopographyTandemXConfig(AppConfig):
-    name = 'topography-tandem-x'
diff --git a/topography-tandem-x/__init__.py b/topography_tandem_x/__init__.py
similarity index 100%
rename from topography-tandem-x/__init__.py
rename to topography_tandem_x/__init__.py
diff --git a/topography-tandem-x/admin.py b/topography_tandem_x/admin.py
similarity index 100%
rename from topography-tandem-x/admin.py
rename to topography_tandem_x/admin.py
diff --git a/topography_tandem_x/apps.py b/topography_tandem_x/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..73ee436a827315f1edb75691d2691a408c41b891
--- /dev/null
+++ b/topography_tandem_x/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class TopographyTandemXConfig(AppConfig):  # pragma: no cover
+    name = 'topography_tandem_x'
diff --git a/topography-tandem-x/migrations/__init__.py b/topography_tandem_x/migrations/__init__.py
similarity index 100%
rename from topography-tandem-x/migrations/__init__.py
rename to topography_tandem_x/migrations/__init__.py
diff --git a/topography-tandem-x/serializers.py b/topography_tandem_x/serializers.py
similarity index 100%
rename from topography-tandem-x/serializers.py
rename to topography_tandem_x/serializers.py
diff --git a/topography-tandem-x/topography_file_extraction.py b/topography_tandem_x/topography_file_extraction.py
similarity index 97%
rename from topography-tandem-x/topography_file_extraction.py
rename to topography_tandem_x/topography_file_extraction.py
index 50525ef46ca7df898bb0f52fecae72b0bf457940..c5eb675da433f03511a1af1ddc26df54398403ca 100644
--- a/topography-tandem-x/topography_file_extraction.py
+++ b/topography_tandem_x/topography_file_extraction.py
@@ -21,7 +21,7 @@ def read_proxydata(filename, dummy=DEBUG and USE_DUMMY_TOPOGRAPHY_TANDEM_DATA):
     if dummy or TEST:
         return create_dummy_data()
 
-    # TOPOGRAPHY_DATA_DIR = os.path.join(DATA_DIR, 'topography-tandem-x')
+    # TOPOGRAPHY_DATA_DIR = os.path.join(DATA_DIR, 'topography_tandem_x')
     TOPOGRAPHY_FILE = os.path.join(DATA_DIR, filename)
 
     if DEBUG:
diff --git a/topography-tandem-x/urls.py b/topography_tandem_x/urls.py
similarity index 100%
rename from topography-tandem-x/urls.py
rename to topography_tandem_x/urls.py
diff --git a/topography-tandem-x/views.py b/topography_tandem_x/views.py
similarity index 100%
rename from topography-tandem-x/views.py
rename to topography_tandem_x/views.py
diff --git a/utils/agg_serializer.py b/utils/agg_serializer.py
index 88ad88e9df7231ae91511e6cadb7a32b59ab9300..aa797bc17048db78f9c4bffb009e7c11fe1037f6 100644
--- a/utils/agg_serializer.py
+++ b/utils/agg_serializer.py
@@ -14,133 +14,55 @@ class GeneralAggSerializer(BaseSerializer):
         # build GeoJSON response with 'provenance' extension
         # ToDo (probably in views): change content-type to application/vnd.geo+json
         # ToDo: enable support for different output formats
-        # format properties depending on 'by_direction' (vector or not)
+
+        # check if single value is returned
         try:
             vlength = len(val)
         except TypeError:
             vlength = 1
+        # either multiple bins or direction is enabled
         if vlength > 1 or obj['direction'] is not None:
+            # return values are bin values
             if obj['direction'] is None:
                 f = OrderedDict([(d, v) for d, v in zip(range(vlength), val)])
             else:
+                # check if there are bins for each direction property
                 try:
                     vlength_sub = len(val[0])
                 except TypeError:
                     vlength_sub = 0
+                # create nested dicts with direction in outer and bins in inner dict
                 if vlength_sub > 1:
-                    f = OrderedDict([(d, OrderedDict([(b, v) for b, v in zip(obj['bins'], v_sub)])) for d, v_sub in zip(obj['direction'], val)  ])
+                    f = OrderedDict([(d, OrderedDict([(b, v) for b, v in zip(obj['bins'], v_sub)])) for d, v_sub in zip(obj['direction'], val)])
+                # only single value for each direction
                 else:
                     if not isinstance(val, list):
                         val = [val]
                     f = OrderedDict([(d, v) for d, v in zip(obj['direction'], val)])
-            properties = OrderedDict([
-                ('agg_function', agg_function),
-                ('many', True),
-                (agg_function, f),
-                ('units', obj['unit']),
-                ('radius', obj['radius']),
-            ])
+            many = True
+        # only single value is returned
         else:
             try:
                 vlength_sub = len(val[0])
             except (IndexError, TypeError):
                 vlength_sub = 1
-            if vlength_sub > 1:
-                val = OrderedDict([(d, v) for d, v in zip(obj['bins'], val[0])])
-            properties = OrderedDict([
-                ('agg_function', agg_function),
-                ('many', False),
-                (agg_function, val),
-                ('units', obj['unit']),
-                ('radius', obj['radius']),
-                ('direction', obj['direction']),
-            ])
-            if obj['direction'] is None:
-                properties.pop('direction')
-
-        response = OrderedDict([
-            ('type', 'Feature'),
-            ('geometry', OrderedDict([
-                ('type', 'Point'),
-                ('coordinates', [obj['lon'], obj['lat']]),
-                ])),
-            ('properties', properties),
-            ('provenance', obj['get_provenance'](obj)),
-        ])
-
-        return response
-
-    def to_representation_old(self, obj):  # pragma: no cover
-        """takes dictionary-like obj and returns geojson compliant structure"""
-        agg_function = obj['agg_function']
-        val = obj[agg_function]
-
-        # build GeoJSON response with 'provenance' extension
-        # ToDo (probably in views): change content-type to application/vnd.geo+json
-        # ToDo: enable support for different output formats
-        # format properties depending on 'by_direction' (vector or not)
-        try:
-            vlength = len(val)
-        except TypeError:
-            vlength = 1
-        if vlength > 1 or obj['direction'] is not None:
-            if obj['direction'] is None:
-                properties = OrderedDict([
-                    ('agg_function', agg_function),
-                    ('many', True),
-                    (agg_function, OrderedDict([
-                        (d, v) for d, v in zip(range(vlength), val)
-                    ])),
-                    ('units', obj['unit']),
-                    ('radius', obj['radius']),
-                ])
+            if vlength_sub > 1:  # leave this for now, but I think this case never happens?!
+                f = OrderedDict([(d, v) for d, v in zip(obj['bins'], val[0])])
+                many = True
             else:
-                try:
-                    vlength_sub = len(val[0])
-                except TypeError:
-                    vlength_sub = 0
-                if vlength_sub > 1:
-                    properties = OrderedDict([
-                        ('agg_function', agg_function),
-                        ('many', True),
-                        (agg_function, OrderedDict([
-                            (d, OrderedDict([
-                                (b, v) for b, v in zip(obj['bins'], v_sub)])
-                             )
-                            for d, v_sub in zip(obj['direction'], val)
-                        ])),
-                        ('units', obj['unit']),
-                        ('radius', obj['radius']),
-                    ])
+                f = val
+                many = False
 
-                else:
-                    properties = OrderedDict([
-                        ('agg_function', agg_function),
-                        ('many', True),
-                        (agg_function, OrderedDict([
-                            (d, v) for d, v in zip(obj['direction'], val)
-                        ])),
-                        ('units', obj['unit']),
-                        ('radius', obj['radius']),
-                    ])
-        else:
-            try:
-                vlength_sub = len(val[0])
-            except (IndexError, TypeError):
-                vlength_sub = 1
-            if vlength_sub > 1:
-                val = OrderedDict([(d, v) for d, v in zip(obj['bins'], val[0])])
-            properties = OrderedDict([
-                ('agg_function', agg_function),
-                ('many', False),
-                (agg_function, val),
-                ('units', obj['unit']),
-                ('radius', obj['radius']),
-                ('direction', obj['direction']),
-            ])
-            if obj['direction'] is None:
-                properties.pop('direction')
+        # create properties
+        properties = OrderedDict([
+            ('agg_function', agg_function),
+            ('many', many),
+            (agg_function, f),
+            ('units', obj['unit']),
+            ('radius', obj['radius']),
+        ])
 
+        # create response
         response = OrderedDict([
             ('type', 'Feature'),
             ('geometry', OrderedDict([
diff --git a/utils/extraction_tools.py b/utils/extraction_tools.py
index a9496001bf3408c42290cdb0baa3a105ba22315e..447b73b42a7942399c57954fcdf002ed72497e48 100755
--- a/utils/extraction_tools.py
+++ b/utils/extraction_tools.py
@@ -6,6 +6,7 @@ get_station_info() - query REST service to obtain names and coordinates of all s
 
 import numpy as np
 from utils.geoutils import standardize_lon180
+from toar_location_services.settings import DEBUG
 
 
 def is_inside(lon, lat, boundingbox):
@@ -104,7 +105,8 @@ def extract_circle(lonvec, latvec, data, ilon, ilat, nlon, nlat,
         if max_angle is None:
             max_angle = 360.
         max_angle = np.deg2rad(standardize_lon180(max_angle))
-        print('#### min_angle, max_angle = ', np.rad2deg(min_angle), np.rad2deg(max_angle))
+        if DEBUG:
+            print('#### min_angle, max_angle = ', np.rad2deg(min_angle), np.rad2deg(max_angle))
 
     res = np.zeros((2 * nlat + 1, 2 * nlon + 1))
     res[:] = np.nan
diff --git a/utils/statistics.py b/utils/statistics.py
index e075427cf682427bf7db787945657c29c4c335a8..87dc33796b2773b774de793a87b64f04f01009bf 100755
--- a/utils/statistics.py
+++ b/utils/statistics.py
@@ -6,12 +6,16 @@ def most_common_value(values):
     return np.bincount(values.astype(int)).argmax()
 
 
-def relative_frequency(values, decimals=6, bins=0, start_bin=0):
-    """Get the relative frequency of each possible class"""
+def relative_frequency(values, decimals=6, bins=0, start_bin=0, missing_value=-999.):
+    """Get the relative frequency of each possible class. There is 1 additional class for missing values."""
+    values = values.astype(float)
+    values[np.where(values == missing_value)] = np.nan
     if bins > 0:
-        f = np.round(np.bincount(values.astype(int), minlength=int(bins)+start_bin) / len(values), decimals)[start_bin:]
+        f = np.round(np.bincount(values[~np.isnan(values)].astype(int), minlength=int(bins)+start_bin) / len(values), decimals)[start_bin:]
     else:
-        f = np.round(np.bincount(values.astype(int)) / len(values), decimals)
+        f = np.round(np.bincount(values[~np.isnan(values)].astype(int)) / len(values), decimals)
+    # collect missing data
+    f = np.append(f, np.round(np.sum(np.isnan(values))/len(values), decimals))
     return f
 
 
diff --git a/utils/tests.py b/utils/tests.py
index ac1e31718dccc6d5e5dab2fdb245c36ccb7d010b..ae5a5d9fefee2d378bf2e45fe3dca88e2a7c29cf 100644
--- a/utils/tests.py
+++ b/utils/tests.py
@@ -37,32 +37,38 @@ class TestStatistics(TestCase):
         f = statistics.relative_frequency
 
         # use default settings
-        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667])
+        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(self.data), expected))
 
         # round on 1 decimal
-        expected = np.array([0.1, 0.4, 0.3, 0., 0.1, 0., 0.1, 0., 0., 0.1])
+        expected = np.array([0.1, 0.4, 0.3, 0., 0.1, 0., 0.1, 0., 0., 0.1, 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(self.data, decimals=1), expected))
 
         # return 12 bins
-        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0., 0.])
+        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0., 0., 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(self.data, bins=12), expected))
 
         # first bin in array is 1, start with bin 0
         data_var = self.data + 1
-        expected = np.array([0., 0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667])
+        expected = np.array([0., 0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(data_var), expected))
 
         # first bin in array is 1, start with bin 0, return 12 bins
         data_var = self.data + 1
-        expected = np.array([0., 0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0.])
+        expected = np.array([0., 0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0., 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(data_var, bins=12), expected))
 
         # first bin in array is 1, start with bin 1
         data_var = self.data + 1
-        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667])
+        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.133333, 0., 0.066667, 0., 0., 0.066667, 0.])
         self.assertIsNone(np.testing.assert_array_equal(f(data_var, start_bin=1, bins=10), expected))
 
+        # use default settings
+        data_var = self.data
+        data_var[3] = -999.
+        expected = np.array([0.066667, 0.4, 0.266667, 0., 0.066667, 0., 0.066667, 0., 0., 0.066667, 0.066667])
+        self.assertIsNone(np.testing.assert_array_equal(f(data_var), expected))
+
     def test_relative_frequency_categorical(self):
         f = statistics.relative_frequency_categorical
 
@@ -640,33 +646,44 @@ class TestCommonViews(TestCase):
         self.assertListEqual(f('abvd'), ['a', 'b', 'v', 'd'])
 
     def test_extract(self):
+        # continuous data
         f = self.view_specific._extract
+        # default
         expected = (4.0, 'value', 'N', None)
         self.assertEqual(f(lon=40, lat=10, radius=None, agg='mean', direction='N'), expected)
+        # out of bounds
         expected = (-888, 'value', 'N', None)
         self.assertEqual(f(lon=6, lat=10, radius=None, agg='mean', direction='N'), expected)
+        # return value because r=0
         expected = (4.0, 'value', 'N', None)
         self.assertEqual(f(lon=40, lat=10, radius=0, agg='mean', direction='N'), expected)
+        # get mean=0
         expected = ([0.0], 'mean', None, None)
         self.assertEqual(f(lon=43, lat=13, radius=150000, agg='mean', direction=None), expected)
+        # get mean=4/9
         expected = ([4/9.], 'mean', None, None)
         self.assertEqual(f(lon=41, lat=11, radius=200000, agg='mean', direction=None), expected)
+        # get mean for each direction
         expected = ([4., 4., 2., 4., 2., 4., 2., 4., 4., 4., 6., 4., 6., 4., 4., 4.], 'mean',
                     ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW',
                      'NNW'], None)
         self.assertEqual(f(lon=40, lat=9, radius=200000, agg='mean', direction=['N', 'NNE', 'NE', 'ENE', 'E', 'ESE',
                                                                                 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW',
                                                                                 'W', 'WNW', 'NW', 'NNW']), expected)
+        # categorical data
         f = self.view_categorical._extract
+        # return maxclass
         expected = ([4.], 'maxclass', None, None)
         self.assertEqual(f(lon=40, lat=9, radius=200000, agg='maxclass', direction=None), expected)
-        expected = (np.array([1./3, 0., 0., 0., 4./9, 0., 0., 0., 2./9, 0., 0.]), 'frequency', None,
-                    list(range(0, 11)))
+        # relative frequency of each bin
+        expected = (np.array([1./3, 0., 0., 0., 4./9, 0., 0., 0., 2./9, 0., 0., 0.]), 'frequency', None,
+                    list(range(0, 11)) + ['nan'])
         response = f(lon=40, lat=9, radius=200000, agg='frequency', direction=None)
         self.assertEqual(response[1:], expected[1:])
         self.assertIsNone(np.testing.assert_array_almost_equal(response[0], expected[0]))
+        # no bin length set, therefore return only up to max found bin class
         f = self.view_categorical_nobins._extract
-        expected = (np.array([1./3, 0., 0., 0., 4./9, 0., 0., 0., 2./9]), 'frequency', None,
+        expected = (np.array([1./3, 0., 0., 0., 4./9, 0., 0., 0., 2./9, 0.]), 'frequency', None,
                     list(range(0, 11)))
         response = f(lon=40, lat=9, radius=200000, agg='frequency', direction=None)
         self.assertEqual(response[1:], expected[1:])
@@ -786,7 +803,7 @@ class TestGeneralAggSerializer(TestCase):
             (4, 2), ])
         properties = OrderedDict([
                 ('agg_function', 'mean'),
-                ('many', False),
+                ('many', True),
                 ('mean', values),
                 ('units', 'TestUnit'),
                 ('radius', 400), ])
diff --git a/utils/views_commons.py b/utils/views_commons.py
index fffab26d014de3251bec523168ac3479138f1ee2..3bf1ba83135c39545403f9ec49538e5782861975 100644
--- a/utils/views_commons.py
+++ b/utils/views_commons.py
@@ -267,7 +267,7 @@ class CommonViews:
                 val = self.get_longitude_param(params)
                 tmp['lon'] = val
             elif k == 'radius':
-                radius_none_by_default = not ('agg' in param_keys or self.service_type == "major-roads")
+                radius_none_by_default = not ('agg' in param_keys or self.service_type == "major_roads")
                 val = self.get_radius_param(params, radius_none_by_default)
                 tmp['radius'] = val
             elif k == 'agg':
@@ -328,6 +328,8 @@ class CommonViews:
                         bins = list(range(self.min_valid, self.max_valid+1))
                     else:
                         bins = self.bins
+                    if len(result_tmp) > len(bins):
+                        bins.append('nan')
 
                 result_list.append(result_tmp)
 
diff --git a/wheat_production/apps.py b/wheat_production/apps.py
index aaa8c337714c876d7da653a051134d10faaeb78e..4f020eba356ed931d9fed84850514a499dbfb5c5 100644
--- a/wheat_production/apps.py
+++ b/wheat_production/apps.py
@@ -1,5 +1,5 @@
 from django.apps import AppConfig
 
 
-class WheatProductionConfig(AppConfig):
+class WheatProductionConfig(AppConfig):  # pragma: no cover
     name = 'wheat_production'