diff --git a/VIPSWeb/static/js/util.js b/VIPSWeb/static/js/util.js
index a0c208de3e07437ac72ccb35edc35ebd754478c3..760530f24b8a5520c7077ef8b112707843ca5559 100755
--- a/VIPSWeb/static/js/util.js
+++ b/VIPSWeb/static/js/util.js
@@ -380,3 +380,38 @@ function isMomentJSAvailable()
 	}
 
 }
+
+/**
+ * Depends on the value of currentLanguage and languageCode in settings.js
+ * @param cropCategory
+ * @returns {String}
+ */
+function getLocalizedCropCategoryName(cropCategory)
+{
+	// Fallback in case nothing works
+	if(cropCategory === null)
+	{
+		return "Unnamed";
+	}
+	// Attempting the following languages (in order): current language, default language, English
+	var languages = [settings.currentLanguage, settings.languageCode, "en"];
+	for(var j in languages)
+	{
+		for(var i in cropCategory.cropCategoryLocalSet)
+		{
+			var localeSet = cropCategory.cropCategoryLocalSet[i];
+			if(localeSet.cropCategoryLocalPK.locale.trim() == languages[j].trim())
+			{
+				return localeSet.localName;
+			}
+		}
+	}
+	// Then we try the latin name
+	if(cropCategory.defaultName !== null 
+			&& cropCategory.defaultName !== "")
+	{
+		return cropCategory.defaultName;
+	}
+	// Then we give up
+	return gettext("Unnamed");
+}
diff --git a/VIPSWeb/templates/settings.js b/VIPSWeb/templates/settings.js
index 5e0d72310d758bbed0261344793472616c61ef33..918e8f69193c4d484c3bdd7353e6aa83babfb866 100755
--- a/VIPSWeb/templates/settings.js
+++ b/VIPSWeb/templates/settings.js
@@ -52,5 +52,8 @@ var settings = {
 	            downloadSVG: gettext("Download SVG vector image"),
 	            contextButtonTitle: gettext("Chart context menu")
 			}
-		}
+		},
+		
+		// The attribution shown in the corner of the map 
+	    MAP_ATTRIBUTION : "&copy; <a href='http://www.openstreetmap.org'>OpenStreetMap</a> contributors"
 };
diff --git a/observations/locale/de/LC_MESSAGES/django.mo b/observations/locale/de/LC_MESSAGES/django.mo
index 9f1651026de521647d7079914362dc39e43581ab..71cbdf3e9d8d54be31066ec4ad8628bc2c1f2845 100755
Binary files a/observations/locale/de/LC_MESSAGES/django.mo and b/observations/locale/de/LC_MESSAGES/django.mo differ
diff --git a/observations/locale/de/LC_MESSAGES/django.po b/observations/locale/de/LC_MESSAGES/django.po
index 26a367570632e5d62c6a2648769a18298d2f7fff..dfb8ad3e5be956419763611e49102334ae948c6f 100755
--- a/observations/locale/de/LC_MESSAGES/django.po
+++ b/observations/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-03-14 16:07-0700\n"
+"POT-Creation-Date: 2018-12-10 12:35+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -26,15 +26,20 @@ msgstr ""
 msgid "View all observations"
 msgstr ""
 
-#: templates/observations/detail.html:32 templates/observations/index.html:53
+#: templates/observations/detail.html:32 templates/observations/index.html:63
+#: templates/observations/index_new.html:46
+#: templates/observations/index_new.html:63
 msgid "Organism"
 msgstr ""
 
-#: templates/observations/detail.html:34 templates/observations/index.html:54
+#: templates/observations/detail.html:34 templates/observations/index.html:64
+#: templates/observations/index_new.html:50
+#: templates/observations/index_new.html:64
 msgid "Crop"
 msgstr ""
 
-#: templates/observations/detail.html:36 templates/observations/index.html:52
+#: templates/observations/detail.html:36 templates/observations/index.html:62
+#: templates/observations/index_new.html:62
 msgid "Time of observation"
 msgstr ""
 
@@ -55,6 +60,7 @@ msgid "Map view not available"
 msgstr ""
 
 #: templates/observations/index.html:25 templates/observations/index.html:28
+#: templates/observations/index_new.html:25
 msgid "Observations"
 msgstr ""
 
@@ -62,14 +68,39 @@ msgstr ""
 msgid "All pests"
 msgstr ""
 
+#: templates/observations/index.html:49
+msgid "From"
+msgstr ""
+
+#: templates/observations/index.html:53
+msgid "To"
+msgstr ""
+
 #: templates/observations/index.html:55
+msgid "Date search"
+msgstr ""
+
+#: templates/observations/index.html:65
+#: templates/observations/index_new.html:65
 msgid "Heading"
 msgstr ""
 
-#: templates/observations/index.html:82
+#: templates/observations/index.html:96
 msgid "Crops"
 msgstr ""
 
-#: templates/observations/index.html:112
+#: templates/observations/index.html:136
 msgid "View details"
 msgstr ""
+
+#: templates/observations/index_new.html:31
+msgid "Current date"
+msgstr ""
+
+#: templates/observations/index_new.html:54
+msgid "Crop categories"
+msgstr ""
+
+#: templates/observations/index_new.html:57
+msgid "Filter"
+msgstr ""
diff --git a/observations/locale/de/LC_MESSAGES/djangojs.mo b/observations/locale/de/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000000000000000000000000000000000..71cbdf3e9d8d54be31066ec4ad8628bc2c1f2845
Binary files /dev/null and b/observations/locale/de/LC_MESSAGES/djangojs.mo differ
diff --git a/observations/locale/de/LC_MESSAGES/djangojs.po b/observations/locale/de/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000000000000000000000000000000000..79356bc1b9ccbc1465b0ed748d077b2b3f388e3f
--- /dev/null
+++ b/observations/locale/de/LC_MESSAGES/djangojs.po
@@ -0,0 +1,43 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-12-10 12:38+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/observations/js/observationList.js:152
+msgid "Observation(s) found at location"
+msgstr ""
+
+#: static/observations/js/observationList.js:168
+msgid "in"
+msgstr ""
+
+#: static/observations/js/observationList.js:263
+msgid "Details"
+msgstr ""
+
+#: static/observations/js/observationList.js:465
+msgid "Days since observation"
+msgstr ""
+
+#: static/observations/js/observationList.js:468
+msgid "Days"
+msgstr ""
+
+#: static/observations/js/observationList.js:470
+msgid "Older"
+msgstr ""
diff --git a/observations/locale/nb/LC_MESSAGES/django.mo b/observations/locale/nb/LC_MESSAGES/django.mo
index 8ba93cf30c28a0c3c566cf5005ce6cd98e7794de..6a62268e9810a07d3b2fff93101609b92ec14374 100755
Binary files a/observations/locale/nb/LC_MESSAGES/django.mo and b/observations/locale/nb/LC_MESSAGES/django.mo differ
diff --git a/observations/locale/nb/LC_MESSAGES/django.po b/observations/locale/nb/LC_MESSAGES/django.po
index 5a30ff61786eb36f9c541f2ae18fd7e9826cc748..1a2d035813cab91f5e1d590177174aa4b59f5eee 100755
--- a/observations/locale/nb/LC_MESSAGES/django.po
+++ b/observations/locale/nb/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-03-14 16:07-0700\n"
+"POT-Creation-Date: 2018-12-10 12:35+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -28,17 +28,22 @@ msgid "View all observations"
 msgstr "Se alle observasjoner"
 
 #: templates/observations/detail.html:32
-#: templates/observations/index.html:53
+#: templates/observations/index.html:63
+#: templates/observations/index_new.html:46
+#: templates/observations/index_new.html:63
 msgid "Organism"
 msgstr "Organisme"
 
 #: templates/observations/detail.html:34
-#: templates/observations/index.html:54
+#: templates/observations/index.html:64
+#: templates/observations/index_new.html:50
+#: templates/observations/index_new.html:64
 msgid "Crop"
 msgstr "Kultur"
 
 #: templates/observations/detail.html:36
-#: templates/observations/index.html:52
+#: templates/observations/index.html:62
+#: templates/observations/index_new.html:62
 msgid "Time of observation"
 msgstr "Observasjonstidspunkt"
 
@@ -60,6 +65,7 @@ msgstr "Kartvisning ikke tilgjengelig"
 
 #: templates/observations/index.html:25
 #: templates/observations/index.html:28
+#: templates/observations/index_new.html:25
 msgid "Observations"
 msgstr "Observasjoner"
 
@@ -67,16 +73,41 @@ msgstr "Observasjoner"
 msgid "All pests"
 msgstr "Alle organismer"
 
+#: templates/observations/index.html:49
+msgid "From"
+msgstr "Fra"
+
+#: templates/observations/index.html:53
+msgid "To"
+msgstr "Til"
+
 #: templates/observations/index.html:55
+msgid "Date search"
+msgstr "Datosøk"
+
+#: templates/observations/index.html:65
+#: templates/observations/index_new.html:65
 msgid "Heading"
 msgstr "Overskrift"
 
-#: templates/observations/index.html:82
+#: templates/observations/index.html:96
 #, fuzzy
 msgid "Crops"
 msgstr "Kultur"
 
-#: templates/observations/index.html:112
+#: templates/observations/index.html:136
 msgid "View details"
 msgstr "Se detaljer"
 
+#: templates/observations/index_new.html:31
+msgid "Current date"
+msgstr "Gjeldende dato"
+
+#: templates/observations/index_new.html:54
+msgid "Crop categories"
+msgstr "Kulturkategorier"
+
+#: templates/observations/index_new.html:57
+msgid "Filter"
+msgstr "Gjør utvalg"
+
diff --git a/observations/locale/nb/LC_MESSAGES/djangojs.mo b/observations/locale/nb/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000000000000000000000000000000000..70cb5edf086bb8d5998d09c2ffa3bfaaf0913090
Binary files /dev/null and b/observations/locale/nb/LC_MESSAGES/djangojs.mo differ
diff --git a/observations/locale/nb/LC_MESSAGES/djangojs.po b/observations/locale/nb/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000000000000000000000000000000000..e7acca774b2e698c7983a8b61435a67068b4d78a
--- /dev/null
+++ b/observations/locale/nb/LC_MESSAGES/djangojs.po
@@ -0,0 +1,44 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-12-10 12:38+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: static/observations/js/observationList.js:152
+msgid "Observation(s) found at location"
+msgstr "Observasjon(er) funnet på lokalitet"
+
+#: static/observations/js/observationList.js:168
+msgid "in"
+msgstr "i"
+
+#: static/observations/js/observationList.js:263
+msgid "Details"
+msgstr "Detaljer"
+
+#: static/observations/js/observationList.js:465
+msgid "Days since observation"
+msgstr "Dager siden observasjon"
+
+#: static/observations/js/observationList.js:468
+msgid "Days"
+msgstr "Dager"
+
+#: static/observations/js/observationList.js:470
+msgid "Older"
+msgstr "Eldre"
+
diff --git a/observations/locale/zh_CN/LC_MESSAGES/django.mo b/observations/locale/zh_CN/LC_MESSAGES/django.mo
index 2ccebfda8096607d428c0e2993b9d9f8a9bb3e1b..5dba12b8a50c6a6307127178d6370a861f97f57a 100755
Binary files a/observations/locale/zh_CN/LC_MESSAGES/django.mo and b/observations/locale/zh_CN/LC_MESSAGES/django.mo differ
diff --git a/observations/locale/zh_CN/LC_MESSAGES/django.po b/observations/locale/zh_CN/LC_MESSAGES/django.po
index 555c8ddc17f851ec567434249956fdf92d1b7eef..3a796fe39ecfb2886bac1b74b5752038b6386e10 100755
--- a/observations/locale/zh_CN/LC_MESSAGES/django.po
+++ b/observations/locale/zh_CN/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-03-14 16:07-0700\n"
+"POT-Creation-Date: 2018-12-10 12:35+0100\n"
 "PO-Revision-Date: 2016-10-27 11:26+0200\n"
 "Last-Translator: \n"
 "Language-Team: \n"
@@ -25,15 +25,20 @@ msgstr "观测"
 msgid "View all observations"
 msgstr "查看所有观测"
 
-#: templates/observations/detail.html:32 templates/observations/index.html:53
+#: templates/observations/detail.html:32 templates/observations/index.html:63
+#: templates/observations/index_new.html:46
+#: templates/observations/index_new.html:63
 msgid "Organism"
 msgstr "生物"
 
-#: templates/observations/detail.html:34 templates/observations/index.html:54
+#: templates/observations/detail.html:34 templates/observations/index.html:64
+#: templates/observations/index_new.html:50
+#: templates/observations/index_new.html:64
 msgid "Crop"
 msgstr "作物"
 
-#: templates/observations/detail.html:36 templates/observations/index.html:52
+#: templates/observations/detail.html:36 templates/observations/index.html:62
+#: templates/observations/index_new.html:62
 msgid "Time of observation"
 msgstr "观测时间"
 
@@ -54,6 +59,7 @@ msgid "Map view not available"
 msgstr ""
 
 #: templates/observations/index.html:25 templates/observations/index.html:28
+#: templates/observations/index_new.html:25
 msgid "Observations"
 msgstr "观测"
 
@@ -61,16 +67,41 @@ msgstr "观测"
 msgid "All pests"
 msgstr "所有病虫害"
 
+#: templates/observations/index.html:49
+msgid "From"
+msgstr ""
+
+#: templates/observations/index.html:53
+msgid "To"
+msgstr ""
+
 #: templates/observations/index.html:55
+msgid "Date search"
+msgstr ""
+
+#: templates/observations/index.html:65
+#: templates/observations/index_new.html:65
 msgid "Heading"
 msgstr "标题"
 
-#: templates/observations/index.html:82
+#: templates/observations/index.html:96
 #, fuzzy
 #| msgid "Crop"
 msgid "Crops"
 msgstr "作物"
 
-#: templates/observations/index.html:112
+#: templates/observations/index.html:136
 msgid "View details"
 msgstr "看细节"
+
+#: templates/observations/index_new.html:31
+msgid "Current date"
+msgstr ""
+
+#: templates/observations/index_new.html:54
+msgid "Crop categories"
+msgstr ""
+
+#: templates/observations/index_new.html:57
+msgid "Filter"
+msgstr ""
diff --git a/observations/locale/zh_CN/LC_MESSAGES/djangojs.mo b/observations/locale/zh_CN/LC_MESSAGES/djangojs.mo
new file mode 100644
index 0000000000000000000000000000000000000000..314bedb17d5caa2d590b5786ba725a4f8d4dab37
Binary files /dev/null and b/observations/locale/zh_CN/LC_MESSAGES/djangojs.mo differ
diff --git a/observations/locale/zh_CN/LC_MESSAGES/djangojs.po b/observations/locale/zh_CN/LC_MESSAGES/djangojs.po
new file mode 100644
index 0000000000000000000000000000000000000000..0e322ed22428a86522d84c295e9564b7ef2a7d2d
--- /dev/null
+++ b/observations/locale/zh_CN/LC_MESSAGES/djangojs.po
@@ -0,0 +1,43 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-12-10 12:38+0100\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: static/observations/js/observationList.js:152
+msgid "Observation(s) found at location"
+msgstr ""
+
+#: static/observations/js/observationList.js:168
+msgid "in"
+msgstr ""
+
+#: static/observations/js/observationList.js:263
+msgid "Details"
+msgstr ""
+
+#: static/observations/js/observationList.js:465
+msgid "Days since observation"
+msgstr ""
+
+#: static/observations/js/observationList.js:468
+msgid "Days"
+msgstr ""
+
+#: static/observations/js/observationList.js:470
+msgid "Older"
+msgstr ""
diff --git a/observations/static/observations/js/observationList.js b/observations/static/observations/js/observationList.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ad744f711e880a43c3be3b4c7ff0090767b137d
--- /dev/null
+++ b/observations/static/observations/js/observationList.js
@@ -0,0 +1,578 @@
+/* 
+ * Copyright (c) 2018 NIBIO <http://www.nibio.no/>. 
+ * 
+ * This file is part of VIPSWeb.
+ * VIPSLogic is free software: you can redistribute it and/or modify
+ * it under the terms of the NIBIO Open Source License as published by 
+ * NIBIO, either version 1 of the License, or (at your option) any
+ * later version.
+ * 
+ * VIPSWeb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * NIBIO Open Source License for more details.
+ * 
+ * You should have received a copy of the NIBIO Open Source License
+ * along with VIPSWeb.  If not, see <http://www.nibio.no/licenses/>.
+ * 
+ */
+
+var allObservations = []; // Populated asynchronously
+var drawnFeatures = []; // Populated asynchronously
+var currentDate; // Initialized in initMap
+var map;
+var observationLayer;
+
+/**
+ * Default coloring
+ * @type Array
+ */
+var ageColors = [
+    [7, 'rgba(233, 13, 0, 1.0)'], // Red #e90d00
+    [30, 'rgba(253, 202, 00, 1.0)'], // Orange #fdca00
+    [60, 'rgba(0, 120, 253, 1.0)'], // Blue #0078fd
+];
+
+
+var ageStyles, poiDetails, popOverlay;
+
+/*
+ * Observation map
+ * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
+ */
+var initMap = function(   
+            center, 
+            zoomLevel, 
+            organizationId,
+            from,
+            to,
+            pestId,
+            cropId,
+            cropCategoryId,
+            customAgeColors
+        )
+{
+	// Allows for dynamic configuration of observation age groups and styles
+    if(typeof customAgeColors !== 'undefined')
+    {
+        ageColors = customAgeColors;
+    }
+    initAgeStyles();
+    
+    // This is the page wide artificial time state of the page
+    currentDateInMillis = moment(to).format("X") * 1000; // Reference for coloring the observations
+    
+    // Background layer is OpenStreetMap
+    var backgroundLayer = new ol.layer.Tile({
+                    source: new ol.source.OSM({
+                        attributions: [
+                            new ol.Attribution({
+                              html: settings.MAP_ATTRIBUTION
+                            })
+                          ]
+                    })
+    });
+    
+    currentDateInMillis = moment().add(settings.systemTimeOffsetMonths,"months").format("X") * 1000; // Reference for coloring the observations
+	var observationFeatures = new ol.Collection(); // Starting on empty   
+    observationLayer = new ol.layer.Vector({
+	    source: new ol.source.Vector({
+	      features: observationFeatures
+	    }),
+	    style: getCorrectStyle // Defined further down
+	  });
+    
+    // Layer for popup
+    popOverlay = new ol.Overlay({
+      element: document.getElementById("popover")
+    });
+
+    // Creating the map
+    map = new ol.Map({
+                    target: 'observationMap',
+                    layers: [backgroundLayer, observationLayer],
+                    overlays: [popOverlay],
+                    renderer: 'canvas'
+    });
+    
+    // For some reason, we have to wait with selecting the layer until after the map has
+    // been initialized. Otherwise the popover will not display.
+    // OpenLayers is probably doing something that changes the DOM object
+    // Using Bootstrap's popover plugin. See http://getbootstrap.com/javascript/#popovers
+    poiDetails = $("#popover");
+    
+    // Setting zoom and center for the map (need to do this after creating map. so that we kan transform our
+    // center to correct map projection)
+    var centerPosition = ol.proj.transform(center, 'EPSG:4326', map.getView().getProjection().getCode());
+    var view = new ol.View({
+            center: centerPosition,
+            zoom:zoomLevel
+    });
+    map.setView(view);
+    
+    // Need to build the query string
+    var params = [];
+    
+    if(from !== "")
+    {
+        params.push("from=" + from);
+    }
+    if(to !== "")
+    {
+        params.push("to=" + to);
+    }
+    if(pestId !== null)
+    {
+        params.push("pestId=" + pestId);
+    }
+    if(cropId !== null)
+    {
+        params.push("cropId=" + cropId);
+    }
+    if(cropCategoryId !== null)
+    {
+        params.push("cropCategoryId=" + cropCategoryId);
+    }
+    
+    // Get observations from backend
+    $.getJSON( "/vipslogicproxy/rest/observation/list/filter/" + organizationId + (params.length > 0 ? "?" + params.join("&") : ""), function( data ) {
+        allObservations = data;
+        renderObservationTable(data);
+        renderObservationFeatures();
+
+    });
+
+    // Clicking on a part of the map with one or more features will result in
+    // the observation features being displayed
+    map.on('singleclick', function(evt) {
+    	var pixel = map.getEventPixel(evt.originalEvent);
+		var coordinate = map.getEventCoordinate(evt.originalEvent);
+		displayFeatureDetails(pixel, coordinate);
+    });
+};
+
+/**
+ * When user click on the map: check if there are observation features at that point,
+ * and them display information about them in a popup window
+ */
+var displayFeatureDetails = function(pixel, coordinate) {
+	var features = [];
+    map.forEachFeatureAtPixel(pixel, function(feature,layer){
+       features.push(feature); 
+    });
+
+    if (features.length > 0) {
+    	titleHTML = gettext("Observation(s) found at location");
+    	var observationsHTML = "<ul style='list-style-type: none; margin: 0; padding: 0;'>";
+        
+    	var observations = [];
+    	for(var i in features)
+    	{
+    		observations.push(getObservation(parseInt(features[i].get("observationId"))));
+    	}
+    	
+    	// Sort observations in descending order
+  	  	observations.sort(function(a,b){
+  		  return a.timeOfObservation >= b.timeOfObservation ? -1 : 1;
+  	  	});
+  	  	
+  	  	// Create HTML list with observations
+      	for(var i in observations){
+    		var observation = observations[i];
+    		var obsDate = moment(observation.timeOfObservation).format("YYYY-MM-DD");
+    		observationsHTML += "<li><i style='color: " + getObservationAgeColor(observation.timeOfObservation) + ";' class='fa fa-square' aria-hidden='true'></i> [" + obsDate + "] <a href='/observations/" + observation.observationId + "' target='new'>" + observation.organismName + " " + gettext ("in") + " " + observation.cropOrganismName.toLowerCase()  + "</a></li>";
+      	}
+      	observationsHTML += "</ul>";
+    	
+        // Position the popup, and hiding it
+  	  	poiDetails.popover('destroy');
+  	  	// Placing and displaying the overlay
+  	  	popOverlay.setPosition(coordinate);
+  	  	poiDetails.popover({
+       		animation: true,
+       		trigger: 'manual',
+       		html: true,
+       		placement: "auto top",
+       		title: titleHTML,
+       		content: observationsHTML
+       	  });
+
+  	  	poiDetails.popover('show');
+    } else {
+    	// If no features at clicked point, hide popup (if any)
+        poiDetails.popover('destroy');
+    }
+};
+
+/**
+ * Shows the observations on the map
+ */
+var renderObservationFeatures = function()
+{
+	var geoJSON = {"type":"FeatureCollection","features":[]};
+    for(var i=0;i<allObservations.length;i++)
+    {
+        var observation = allObservations[i];
+        var obsFeatures = null;
+        
+        // Showing only publicly shared observations with geolocation information
+        if(!observation.locationIsPrivate && observation.geoInfo !== null && observation.geoInfo.trim() !== "")
+        {
+            obsFeatures = JSON.parse(observation.geoInfo).features;
+        }
+        else
+        {
+            continue;
+        }
+        
+        for(var j=0; j<obsFeatures.length; j++)
+        {
+            geoJSON.features.push(obsFeatures[j]);
+        }
+    }
+
+    var format = new ol.format.GeoJSON();
+    drawnfeatures = format.readFeatures(geoJSON, {
+      dataProjection: 'EPSG:4326',
+      featureProjection: map.getView().getProjection().getCode()
+    });
+    observationLayer.getSource().clear();
+    observationLayer.getSource().addFeatures(drawnfeatures);
+}
+
+/**
+ * Pull the observation from global list
+ */
+var getObservation = function(observationId)
+{
+    for(var i=0; i<allObservations.length;i++)
+    {
+        if(allObservations[i].observationId == parseInt(observationId))
+        {
+            return allObservations[i];
+        }
+    }
+    //console.info("Could not find observation with ID=" + observationId)
+    return null;
+};
+
+/**
+ * Show the table of observations
+ */
+var renderObservationTable = function(data)
+{
+    var tbody = document.getElementById("observationTableBody");
+    var tbodyHTML = [];
+    for(var i=0; i<data.length;i++)
+    {
+        var obs = data[i];
+        tbodyHTML.push("<tr>");
+        tbodyHTML.push("<td>" + moment(obs.timeOfObservation).format("YYYY-MM-DD HH:mm ZZ") + "</td>");
+        tbodyHTML.push("<td>" + obs.organismName + "</td>");
+        tbodyHTML.push("<td>" + obs.cropOrganismName + "</td>");
+        tbodyHTML.push("<td>" + obs.observationHeading + "</td>");
+        tbodyHTML.push("<td><a href='/observations/" + obs.observationId + "' target='new'>" + gettext("Details") + "</a></td>");
+        tbodyHTML.push("</tr>");
+    }
+    tbody.innerHTML = tbodyHTML.join("\n");
+};
+
+/** 
+ * Render the select field for pest or crop
+ */
+var renderOrganismField = function(organismList, fieldId, selectedId)
+{
+    // Sort alphabetically by local name
+    organismList.sort(function(a,b){
+        if (getLocalizedOrganismName(a) < getLocalizedOrganismName(b)) return -1;
+        if (getLocalizedOrganismName(a) > getLocalizedOrganismName(b)) return 1;
+        return 0;
+    });
+    var list = document.getElementById(fieldId);
+    list.options.length=0;
+    list.options[0] = new Option("",""); // For the chosenjs to print data-placeholder 
+    for(var i=0;i<organismList.length;i++)
+    {
+        var organism = organismList[i];
+        var newOption = new Option(getLocalizedOrganismName(organism),organism.organismId);
+        if(organism.organismId === selectedId)
+        {
+            newOption.selected = true;
+        }
+        list.options[list.options.length] = newOption;
+    }
+};
+
+/**
+ * Render the crop category select field
+ */
+var renderCropCategoryField = function(cropCategoryList, selectedId)
+{
+    // Sort by local name
+    cropCategoryList.sort(function(a,b){
+        if (getLocalizedCropCategoryName(a) < getLocalizedCropCategoryName(b)) return -1;
+        if (getLocalizedCropCategoryName(a) > getLocalizedCropCategoryName(b)) return 1;
+        return 0;
+    });
+    var list = document.getElementById("cropCategoryList");
+    list.options.length=0;
+    list.options[0] = new Option("",""); // For the chosenjs to print data-placeholder 
+    for(var i=0;i<cropCategoryList.length;i++)
+    {
+        var cropCategory = cropCategoryList[i];
+        var newOption = new Option(getLocalizedCropCategoryName(cropCategory),cropCategory.cropCategoryId);
+        if(cropCategory.cropCategoryId === selectedId)
+        {
+            newOption.selected = true;
+        }
+        list.options[list.options.length] = newOption;
+    }
+};
+
+var initForm = function(organizationId,
+            pestId,
+            cropId,
+            cropCategoryId,
+            postRenderFormActions
+        )
+{
+    $.getJSON( "/vipslogicproxy/rest/observation/pest/" + organizationId , function( pestList ) {
+        renderOrganismField(pestList, "observationPestList", pestId);
+        $.getJSON( "/vipslogicproxy/rest/observation/crop/" + organizationId , function( cropList ) {
+            renderOrganismField(cropList, "observationCropList", cropId);
+             $.getJSON( "/vipslogicproxy/rest/organism/cropcategory/" + organizationId , function( cropCategoryList ) {
+                renderCropCategoryField(cropCategoryList, cropCategoryId);
+                postRenderFormActions(); // Activate chosen.js
+            });
+        });
+    });
+};
+
+// Global configs
+/**
+ * Default style for old (outdated) observations
+ */
+var styleOld = 
+    new ol.style.Style({
+          fill: new ol.style.Fill({
+            color: 'rgba(173, 173, 173, 0.5)'
+          }),
+          stroke: new ol.style.Stroke({
+            color: '#000000',
+            width: 1
+          }),
+          image: new ol.style.Circle({
+            radius: 6,
+            fill: new ol.style.Fill({
+              color: '#adadad'
+            }),
+            stroke: new ol.style.Stroke({
+                color: '#000000',
+                width: 1
+              })
+          })
+        });
+
+/**
+ * If an observation should be hidden entirely
+ */
+  var styleInvisible = new ol.style.Style({
+                fill: new ol.style.Fill({
+                  color: 'rgba(0, 0, 0, 0.0)'
+                }),
+                stroke: new ol.style.Stroke({
+                  color: 'rgba(0, 0, 0, 0.0)',
+                  width: 0
+                }),
+                image: new ol.style.Circle({
+                  radius: 0,
+                  fill: new ol.style.Fill({
+                    color: 'rgba(0, 0, 0, 0.0)'
+                  })
+                })
+              });
+
+  /**
+   * Using config at top of file (or customAgeColors passed in from web page) for dynamic style creation
+   */
+  var initAgeStyles = function(){
+      ageStyles = [];
+      for(var i in ageColors)
+      {
+          ageStyles.push([
+              ageColors[i][0],
+              new ol.style.Style({
+                  fill: new ol.style.Fill({
+                    color: ageColors[i][1].replace("1.0","0.2")
+                  }),
+                  stroke: new ol.style.Stroke({
+                    color: ageColors[i][1],
+                    width: 1
+                  }),
+                  image: new ol.style.Circle({
+                    radius: 6,
+                    fill: new ol.style.Fill({
+                      color: ageColors[i][1]
+                    }),
+                    stroke: new ol.style.Stroke({
+                        color: '#000000',
+                        width: 1
+                      })
+                  })
+                })
+            ]); 
+      }
+  };
+
+  /**
+   * Based on the observation's age relative to "currentDate", choose the appropriate style for map rendering
+   */
+var getCorrectStyle = function(feature){
+            var age = getObservationRelativeAge(feature);
+            if(age == null)
+            {
+                return;
+            }
+            if(age < 0)
+            {
+                return styleInvisible;
+            }
+            for(var i in ageStyles)
+            {
+                if(age < ageStyles[i][0])
+                {
+                    return ageStyles[i][1];
+                }
+            }
+            return styleOld;
+        };
+
+/**
+ * Returns the observation's age in days relative to the "current date", which is defined by the value of the "dayInPeriod" range input
+ */
+var getObservationRelativeAge = function(feature)
+{
+    var observation = getObservation(feature.get("observationId")); // TODO include observations timestamp in feature. Speeds things up
+    if(observation == null)
+    {
+        console.info("No obs with id=" + feature.get("observationId"));
+        return null; // Means invisible
+    }
+    return Math.floor((currentDateInMillis - getUnixTimestampFromJSON(observation.timeOfObservation)) / (1000 * 60 * 60 * 24)) + 1;
+};
+
+/**
+ * Using configured ageColors 
+ * @param aDate
+ * @returns
+ */
+function getObservationAgeColor(aDate)
+{
+	var age = getDaysSince(aDate);
+    if(age == null)
+    {
+        return;
+    }
+    if(age < 0)
+    {
+        return styleInvisible.getFill().getColor();
+    }
+    for(var i in ageColors)
+    {
+        if(age < ageColors[i][0])
+        {
+            return ageColors[i][1];
+        }
+    }
+    return styleOld.getFill().getColor();
+}
+
+/**
+ * Using current system time, counting days since this day
+ */
+var getDaysSince = function(JSONDate)
+{
+	return Math.floor((currentDateInMillis - getUnixTimestampFromJSON(JSONDate)) / (1000 * 60 * 60 * 24)) + 1;
+};
+
+/**
+ * Sets colors and values for the map legend
+ * @returns
+ */
+function initMapLegend()
+{
+    var lBox = document.getElementById("legend");
+    var html = "<div><strong>" + gettext("Days since observation") + "</strong></div><ul>";
+    for(var i in ageColors)
+    {
+            html += '<li><i style="color: ' + ageColors[i][1] + ';" class="fa fa-square" aria-hidden="true"></i> ' + (i > 0 ? ageColors[i-1][0] + 1 : '0') + '-' + ageColors[i][0] + ' ' + gettext("Days").toLowerCase() + '</li>';
+    }
+        html += '<li><i style="color: black;" class="fa fa-square" aria-hidden="true"></i> ' + gettext("Older") + '</li>';
+    html += "</ul>";
+    lBox.innerHTML = html;
+}
+    
+var from = moment(document.getElementById("dateFrom").value);
+
+/**
+ * Sets the current date in the observation "player"
+ * @param rangeBar
+ * @returns
+ */
+function updateCurrentDate(rangeBar){
+    currentDate = from.clone();
+    currentDate.add(parseInt(rangeBar.value)-1,"days");
+    document.getElementById("dayInPeriodDate").innerHTML=currentDate.format('YYYY-MM-DD');    
+    currentDateInMillis = currentDate.format("X") * 1000;
+    observationLayer.getSource().changed();
+    //console.info(currentDate);
+}
+    
+/**
+ * Updates the map observations given the date
+ * @param rangeBar
+ * @returns
+ */
+function updateMap(rangeBar){
+    currentDate = from.clone();
+    currentDate.add(parseInt(rangeBar.value)-1,"days");
+    currentDateInMillis = currentDate.format("X") * 1000;
+    observationLayer.getSource().changed();
+}
+
+/**
+ * Moves everything one day forward
+ */
+var moveCurrentDayForward = function(){
+    theRange = document.getElementById("dayInPeriod");
+    if(parseInt(theRange.value) < theRange.max)
+    {
+        theRange.value = parseInt(theRange.value) +1;
+        theRange.oninput();
+    }
+    else{
+        togglePlay(document.getElementById("playButton"));
+    }
+}
+
+// Placeholder for the JS timer function called for moving time ("dayInPeriod" range input) forward
+var intervalId = null; // See below
+
+/**
+ * Switches between play and pause
+ * @param theButton
+ * @returns
+ */
+function togglePlay(theButton){
+    if(intervalId == null)
+    {
+        intervalId = setInterval(moveCurrentDayForward, 250);
+        theButton.innerHTML = '<i class="fa fa-pause" aria-hidden="true"></i>';
+    }
+    else
+    {
+        clearInterval(intervalId);
+        intervalId = null;
+        theButton.innerHTML = '<i class="fa fa-play" aria-hidden="true"></i>';
+    }
+}
\ No newline at end of file
diff --git a/observations/templates/observations/index.html b/observations/templates/observations/index.html
old mode 100755
new mode 100644
index 1ae0f0297654e13c936057e5374ac74a2c96c6ed..7049fcc911a1f51eccf1aedb48ce0e8fb76bd9bc
--- a/observations/templates/observations/index.html
+++ b/observations/templates/observations/index.html
@@ -3,7 +3,7 @@
 {% comment %}
 
 #
-# Copyright (c) 2016 NIBIO <http://www.nibio.no/>. 
+# Copyright (c) 2018 NIBIO <http://www.nibio.no/>. 
 # 
 # This file is part of VIPSWeb.
 # VIPSWeb is free software: you can redistribute it and/or modify
@@ -21,193 +21,120 @@
 # 
  
 {% endcomment %}
-{% load i18n %}
+{% load i18n l10n %}
 {% block title%}{% trans "Observations" %}{%endblock%}
 {% block content %}
-
-	<h1>{% trans "Observations" %}</h1>
-	<div class="row">
-		<div class="col-xs-3">
-			<div class="form-group">
-				<select id="pestFilterList" class="form-control" onchange="filterObservations();">
-					<option value="-1">{% trans "All pests" %}</option>
-				</select>
-			</div>
-		</div>
-		<div class="col-xs-3">
-			<div class="form-group">
-				<select name="cropCategoryFilterList" id="cropCategoryFilterList" class="form-control chosen-select" multiple="multiple" onchange="filterObservations();">
-					{% for crop_category in crop_categories|dictsort:"name" %}
-					<option value="{{crop_category.crop_category_id}}">{{crop_category.name}}</option>
-					{% endfor %}			
-				</select>
-			</div>
-		</div>
-		<div class="col-xs-6">
-		<form class="form-inline" method="get" action="/observations">
-			<div class="form-group">
-				<input class="form-control" type="date" name="{{form.timeOfObservationFrom.html_name}}" value="{{form.timeOfObservationFrom.value | default_if_none:""}}" placeholder="{% trans "From" %}"/>
-			</div>
-			-
-			<div class="form-group">
-				<input class="form-control" type="date" name="{{form.timeOfObservationTo.html_name}}" value="{{form.timeOfObservationTo.value | default_if_none:""}}" placeholder="{% trans "To" %}"/>
-			</div>
-			<button type="submit" class="btn btn-primary">{% trans "Date search" %}</button>
-		</form>
-	</div>
+<h1>{% trans "Observations" %}</h1>
+<div id="observationMap" class="map" style="position:relative;">
+	<div id="popover"></div>
+	<div class="form-group" id="progressBar">
+	    <label for="dayInPeriod">{% trans "Current date"%}: <span id="dayInPeriodDate">{{to|safe}}</span></label><br/>
+	    <button type="button" id="playButton" class="btn" onclick="togglePlay(this);"><i class="fa fa-play" aria-hidden="true"></i></button>
+	    <input type="range" id="dayInPeriod" name="dayInPeriod" min="1" max="{{period_days}}" step="1" value="{{period_days}}" oninput="updateCurrentDate(this);" onchange="updateMap(this);"/>
 	</div>
-	<table class="table">
-		<thead>
-			<tr>
-				<th>{% trans "Time of observation" %}</th>
-				<th>{% trans "Organism" %}</th>
-				<th>{% trans "Crop" %}</th>
-				<th>{% trans "Heading" %}</th>
-				<th></th>
-			</tr>
-		</thead>
-		<tbody id="observationList"></tbody>
-	</table>
-
+	<div id="legend"></div>
+</div>
+<form class="form-inline" method="get" action="">
+            <div class="form-group">
+                <input class="form-control" type="date" id="dateFrom" name="from" value="{{from|safe}}"/>
+            </div>
+            -
+            <div class="form-group">
+                <input class="form-control" type="date" id="dateTo" name="to" value="{{to|safe}}"/>
+            </div>
+            <div class="form-group">
+                    <select name="pestId" id="observationPestList" class="form-control chosen-select" data-placeholder="{% trans "Organism" %}">
+                    </select>
+            </div>
+            <div class="form-group">
+                    <select name="cropId" id="observationCropList" class="form-control chosen-select" data-placeholder="{% trans "Crop" %}">	
+                    </select>
+            </div>
+            <div class="form-group">
+                    <select name="cropCategoryId" id="cropCategoryList" class="form-control chosen-select" data-placeholder="{% trans "Crop categories" %}">	
+                    </select>
+            </div>
+            <button type="submit" class="btn btn-default">{% trans "Filter" %}</button>
+        </form>
+<div class="table-responsive">
+            <table class="table table-striped" id="observationTable">
+                    <thead>
+                            <th>{% trans "Time of observation" %}</th>
+                            <th>{% trans "Organism" %}</th>
+                            <th>{% trans "Crop" %}</th>
+                            <th>{% trans "Heading" %}</th>
+                            <th></th>
+                    </thead>
+                    <tbody id="observationTableBody">
+                    </tbody>
+            </table>
+        </div>
 {% endblock %}
 {% block extendCSS %}
-<link href="{% static "css/3rdparty/jquery-ui.min.css" %}" rel="stylesheet" media="screen" />
+<link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css">
 <link href="{% static "css/3rdparty/chosen.min.css" %}" rel="stylesheet" media="screen" />
 <link href="{% static "css/3rdparty/chosen-bootstrap.css" %}" rel="stylesheet" media="screen" />
+<style type="text/css">
+    #progressBar{
+        position: absolute;
+        bottom: 5px;
+        left: 10px;
+        z-index: 1000;
+        background-color: white;
+        border-radius: 10px;
+        padding: 10px;
+    }
+    #legend{
+        position: absolute;
+        bottom: 115px;
+        left: 10px;
+        z-index: 1000;
+        background-color: white;
+        border-radius: 10px;
+        padding: 10px;
+    }
+    #legend ul {
+        list-style: none;
+        padding:0;
+        margin:0;
+    }
+</style>
 {% endblock %}
 {% block customJS %}
 <script type="text/javascript" src="{% url "django.views.i18n.javascript_catalog" %}"></script>
-<script type="text/javascript" src="{% static "js/3rdparty/modernizr_custom.js" %}"></script>
-<script type="text/javascript" src="{% static "js/3rdparty/jquery-ui.min.js" %}"></script>
-<script type="text/javascript" src="{% static "js/3rdparty/chosen.jquery.min.js" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/ol-debug.js" %}"></script>
 <script type="text/javascript" src="{% static "js/3rdparty/moment.min.js" %}"></script>
-<script type="text/javascript" src="{% url "views.settings_js" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/chosen.jquery.min.js" %}"></script>
 <script type="text/javascript" src="{% static "js/util.js" %}"></script>
+<script type="text/javascript" src="{% url "views.settings_js" %}"></script>
+<script type="text/javascript" src="{% static "observations/js/observationList.js" %}"></script>
 <script type="text/javascript">
-	var allObservations = [];
-	var allPests = [];
-	var allPestIds = [];
-	var cropCategories = {
-			{% for crop_category in crop_categories %}
-			{{crop_category.crop_category_id}}:{{crop_category.crop_ids}}{% if not forloop.last %},{% endif %}
-			{% endfor %}
-	};
+	/*var colors = [
+	    [7, 'rgba(255, 0, 0, 1.0)'], // Red
+	    [30, 'rgba(244, 206, 66, 1.0)'], // Orange
+	    [60, 'rgba(0, 0, 255, 1.0)'], // Blue
+	];*/
 	$(document).ready(function() {
-		$(".chosen-select").chosen({placeholder_text_multiple:"{% trans "Crops" %}"});
-		{% if form.timeOfObservationFrom.value or form.timeOfObservationTo.value %}
-		$.getJSON("/vipslogicproxy/rest/observation/broadcast/list/" + settings.vipsOrganizationId+ "?timeOfObservationFrom={{form.timeOfObservationFrom.value | default_if_none:""}}&timeOfObservationTo={{form.timeOfObservationTo.value | default_if_none:""}}", function( json ) {
-		{% else %}
-		$.getJSON("/vipslogicproxy/rest/observation/broadcast/list/" + settings.vipsOrganizationId+ "?season=" + getCurrentYear(), function( json ) {
-		{% endif %}
-			allObservations = json;
-			allObservations.sort(sortObservationMessages).reverse();
-			for(var i in allObservations)
-			{
-				var currentOrganism = allObservations[i].organism;
-				if(allPestIds.indexOf(currentOrganism.organismId) < 0)
-				{
-					allPests.push(currentOrganism);
-					allPestIds.push(currentOrganism.organismId);
-				}
-			}
-			renderPestFilterList();
-			renderObservations(allObservations);
-		});
-		
-		// Make sure that there is a date picker present for HTML5 
-		// date input fields
-		if (!Modernizr.inputtypes.date) {
-		    $('input[type=date]').datepicker(settings.datePickerConfig);
-		}
+		initMap(
+				[{{settings.MAP_CENTER_LONGITUDE|unlocalize}},{{settings.MAP_CENTER_LATITUDE|unlocalize}}],
+				4,
+				{{settings.VIPS_ORGANIZATION_ID}},
+				"{{from|safe}}",
+				"{{to|safe}}",
+				{{pest_id|default:"null"}},
+				{{crop_id|default:"null"}},
+				{{crop_category_id|default:"null"}}
+				);
 	});
 	
+	initMapLegend();
 	
-	function renderObservations(observations)
-	{
-		var tableHTML = "";
-		for(var i in observations){
-			var observation = observations[i];
-			tableHTML += [
-							"<tr>",
-							"<td>",getSimpleFormattedTimestamp(observation.timeOfObservation),"</td>",
-							"<td>", getLocalizedOrganismName(observation.organism)," <i>(", observation.organism.latinName, ")</i></td>",
-							"<td>", getLocalizedOrganismName(observation.cropOrganism) ," <i>(", observation.cropOrganism.latinName, ")</i></td>",
-							"<td>", observation.observationHeading, "</td>",
-							"<td><a href='/observations/", observation.observationId, "'>{% trans "View details" %}</a></td>",
-							"</tr>"
-							].join("");
-		}
-		document.getElementById("observationList").innerHTML = tableHTML;
-	}
-	
-	function renderPestFilterList()
-	{
-		var pestFilterList = document.getElementById("pestFilterList");
-		pestFilterList.options.length = 1; 
-		allPests.sort(sortOrganisms);
-		for(var i in allPests)
-		{
-			var pestOpt = new Option(getLocalizedOrganismName(allPests[i]), allPests[i].organismId);
-			pestFilterList.options[pestFilterList.options.length] = pestOpt;
-		}
-		
-	}
-
-	function filterOnCropGroup(cropGroupFilterList)
-	{
-		var selectedCropGroupId = parseInt(cropGroupFilterList.options[cropGroupFilterList.options.selectedIndex].value);
-	}
-	
-	function filterObservations()
-	{
-		var pestFilterList = document.getElementById("pestFilterList");
-		var cropCategoryFilterList = document.getElementById("cropCategoryFilterList");
-		
-		var selectedPestOrganismId = parseInt(pestFilterList.options[pestFilterList.options.selectedIndex].value);
-		
-		var selectedCropCategoryIds = getMultipleSelectedValues(cropCategoryFilterList);
-		//console.info(selectedCropCategoryIds);
-		
-		var filteredObservations = [];
-		
-		for(var i=0;i<allObservations.length;i++)
-		{
-			var obs = allObservations[i];
-		
-			var validCropCategory = false;
-			if(selectedCropCategoryIds.length > 0)
-			{			
-				for(var j=0;j<selectedCropCategoryIds.length;j++)
-				{
-					if(cropCategories[selectedCropCategoryIds[j]].indexOf(obs.cropOrganism.organismId) >= 0)
-					{
-						validCropCategory = true;
-					}
-				} 
-			}
-			else
-			{
-				validCropCategory = true;
-			}
-			
-			var validPest = false;
-			if(selectedPestOrganismId == -1)
-			{
-				validPest = true;
-			}
-			else if(obs.organism.organismId == selectedPestOrganismId)
-			{
-				validPest = true;
-			}
-			
-			if(validCropCategory && validPest)
-			{
-				filteredObservations.push(obs);
-			}
-		}
-		
-		renderObservations(filteredObservations);
-	}
-	
+	initForm({{settings.VIPS_ORGANIZATION_ID}},
+			{{pest_id|default:"null"}},
+			{{crop_id|default:"null"}},
+			{{crop_category_id|default:"null"}},
+            function() {$(".chosen-select").chosen({allow_single_deselect: true});} // Must do this after select lists have been populated
+        );
 </script>
+
 {% endblock %}
\ No newline at end of file
diff --git a/observations/templates/observations/index_new.html b/observations/templates/observations/index_new.html
deleted file mode 100644
index 0f713b6058e603f3215a9a7dc676a63d4db9ab46..0000000000000000000000000000000000000000
--- a/observations/templates/observations/index_new.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends "base.html" %}
-{% load staticfiles %}
-{% comment %}
-
-#
-# Copyright (c) 2018 NIBIO <http://www.nibio.no/>. 
-# 
-# This file is part of VIPSWeb.
-# VIPSWeb is free software: you can redistribute it and/or modify
-# it under the terms of the NIBIO Open Source License as published by 
-# NIBIO, either version 1 of the License, or (at your option) any
-# later version.
-# 
-# VIPSWeb is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# NIBIO Open Source License for more details.
-#
-# You should have received a copy of the NIBIO Open Source License
-# along with VIPSWeb.  If not, see <http://www.nibio.no/licenses/>.
-# 
- 
-{% endcomment %}
-{% load i18n %}
-{% block title%}{% trans "Observations" %}{%endblock%}
-{% block content %}
-<div id="observationMap" class="map" style="position:relative;">
-            <div id="popover"></div>
-            <div class="form-group" id="progressBar">
-                <label for="dayInPeriod">${i18nBundle.currentDate}: <span id="dayInPeriodDate">${to?date}</span></label><br/>
-                <button type="button" id="playButton" class="btn" onclick="togglePlay(this);"><i class="fa fa-play" aria-hidden="true"></i></button>
-                <input type="range" id="dayInPeriod" name="dayInPeriod" min="1" max="${periodDays}" step="1" value="${periodDays}" oninput="updateCurrentDate(this);" onchange="updateMap(this)"/>
-                
-            </div>
-            <div id="legend"></div>
-        </div>
-{% endblock %}
-{% block extendCSS %}
-<link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css">
-<style type="text/css">
-    #progressBar{
-        position: absolute;
-        bottom: 5px;
-        left: 10px;
-        z-index: 1000;
-        background-color: white;
-        border-radius: 10px;
-        padding: 10px;
-    }
-    #legend{
-        position: absolute;
-        bottom: 115px;
-        left: 10px;
-        z-index: 1000;
-        background-color: white;
-        border-radius: 10px;
-        padding: 10px;
-    }
-    #legend ul {
-        list-style: none;
-        padding:0;
-        margin:0;
-    }
-</style>
-{% endblock %}
-{% block customJS %}
-<script type="text/javascript" src="{% static "js/3rdparty/ol.js" %}"></script>
-<script type="text/javascript" src="{% static "observations/js/observationMap.js" %}"></script>
-{% endblock %}
\ No newline at end of file
diff --git a/observations/templates/observations/index_old.html b/observations/templates/observations/index_old.html
new file mode 100755
index 0000000000000000000000000000000000000000..1ae0f0297654e13c936057e5374ac74a2c96c6ed
--- /dev/null
+++ b/observations/templates/observations/index_old.html
@@ -0,0 +1,213 @@
+{% extends "base.html" %}
+{% load staticfiles %}
+{% comment %}
+
+#
+# Copyright (c) 2016 NIBIO <http://www.nibio.no/>. 
+# 
+# This file is part of VIPSWeb.
+# VIPSWeb is free software: you can redistribute it and/or modify
+# it under the terms of the NIBIO Open Source License as published by 
+# NIBIO, either version 1 of the License, or (at your option) any
+# later version.
+# 
+# VIPSWeb is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# NIBIO Open Source License for more details.
+#
+# You should have received a copy of the NIBIO Open Source License
+# along with VIPSWeb.  If not, see <http://www.nibio.no/licenses/>.
+# 
+ 
+{% endcomment %}
+{% load i18n %}
+{% block title%}{% trans "Observations" %}{%endblock%}
+{% block content %}
+
+	<h1>{% trans "Observations" %}</h1>
+	<div class="row">
+		<div class="col-xs-3">
+			<div class="form-group">
+				<select id="pestFilterList" class="form-control" onchange="filterObservations();">
+					<option value="-1">{% trans "All pests" %}</option>
+				</select>
+			</div>
+		</div>
+		<div class="col-xs-3">
+			<div class="form-group">
+				<select name="cropCategoryFilterList" id="cropCategoryFilterList" class="form-control chosen-select" multiple="multiple" onchange="filterObservations();">
+					{% for crop_category in crop_categories|dictsort:"name" %}
+					<option value="{{crop_category.crop_category_id}}">{{crop_category.name}}</option>
+					{% endfor %}			
+				</select>
+			</div>
+		</div>
+		<div class="col-xs-6">
+		<form class="form-inline" method="get" action="/observations">
+			<div class="form-group">
+				<input class="form-control" type="date" name="{{form.timeOfObservationFrom.html_name}}" value="{{form.timeOfObservationFrom.value | default_if_none:""}}" placeholder="{% trans "From" %}"/>
+			</div>
+			-
+			<div class="form-group">
+				<input class="form-control" type="date" name="{{form.timeOfObservationTo.html_name}}" value="{{form.timeOfObservationTo.value | default_if_none:""}}" placeholder="{% trans "To" %}"/>
+			</div>
+			<button type="submit" class="btn btn-primary">{% trans "Date search" %}</button>
+		</form>
+	</div>
+	</div>
+	<table class="table">
+		<thead>
+			<tr>
+				<th>{% trans "Time of observation" %}</th>
+				<th>{% trans "Organism" %}</th>
+				<th>{% trans "Crop" %}</th>
+				<th>{% trans "Heading" %}</th>
+				<th></th>
+			</tr>
+		</thead>
+		<tbody id="observationList"></tbody>
+	</table>
+
+{% endblock %}
+{% block extendCSS %}
+<link href="{% static "css/3rdparty/jquery-ui.min.css" %}" rel="stylesheet" media="screen" />
+<link href="{% static "css/3rdparty/chosen.min.css" %}" rel="stylesheet" media="screen" />
+<link href="{% static "css/3rdparty/chosen-bootstrap.css" %}" rel="stylesheet" media="screen" />
+{% endblock %}
+{% block customJS %}
+<script type="text/javascript" src="{% url "django.views.i18n.javascript_catalog" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/modernizr_custom.js" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/jquery-ui.min.js" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/chosen.jquery.min.js" %}"></script>
+<script type="text/javascript" src="{% static "js/3rdparty/moment.min.js" %}"></script>
+<script type="text/javascript" src="{% url "views.settings_js" %}"></script>
+<script type="text/javascript" src="{% static "js/util.js" %}"></script>
+<script type="text/javascript">
+	var allObservations = [];
+	var allPests = [];
+	var allPestIds = [];
+	var cropCategories = {
+			{% for crop_category in crop_categories %}
+			{{crop_category.crop_category_id}}:{{crop_category.crop_ids}}{% if not forloop.last %},{% endif %}
+			{% endfor %}
+	};
+	$(document).ready(function() {
+		$(".chosen-select").chosen({placeholder_text_multiple:"{% trans "Crops" %}"});
+		{% if form.timeOfObservationFrom.value or form.timeOfObservationTo.value %}
+		$.getJSON("/vipslogicproxy/rest/observation/broadcast/list/" + settings.vipsOrganizationId+ "?timeOfObservationFrom={{form.timeOfObservationFrom.value | default_if_none:""}}&timeOfObservationTo={{form.timeOfObservationTo.value | default_if_none:""}}", function( json ) {
+		{% else %}
+		$.getJSON("/vipslogicproxy/rest/observation/broadcast/list/" + settings.vipsOrganizationId+ "?season=" + getCurrentYear(), function( json ) {
+		{% endif %}
+			allObservations = json;
+			allObservations.sort(sortObservationMessages).reverse();
+			for(var i in allObservations)
+			{
+				var currentOrganism = allObservations[i].organism;
+				if(allPestIds.indexOf(currentOrganism.organismId) < 0)
+				{
+					allPests.push(currentOrganism);
+					allPestIds.push(currentOrganism.organismId);
+				}
+			}
+			renderPestFilterList();
+			renderObservations(allObservations);
+		});
+		
+		// Make sure that there is a date picker present for HTML5 
+		// date input fields
+		if (!Modernizr.inputtypes.date) {
+		    $('input[type=date]').datepicker(settings.datePickerConfig);
+		}
+	});
+	
+	
+	function renderObservations(observations)
+	{
+		var tableHTML = "";
+		for(var i in observations){
+			var observation = observations[i];
+			tableHTML += [
+							"<tr>",
+							"<td>",getSimpleFormattedTimestamp(observation.timeOfObservation),"</td>",
+							"<td>", getLocalizedOrganismName(observation.organism)," <i>(", observation.organism.latinName, ")</i></td>",
+							"<td>", getLocalizedOrganismName(observation.cropOrganism) ," <i>(", observation.cropOrganism.latinName, ")</i></td>",
+							"<td>", observation.observationHeading, "</td>",
+							"<td><a href='/observations/", observation.observationId, "'>{% trans "View details" %}</a></td>",
+							"</tr>"
+							].join("");
+		}
+		document.getElementById("observationList").innerHTML = tableHTML;
+	}
+	
+	function renderPestFilterList()
+	{
+		var pestFilterList = document.getElementById("pestFilterList");
+		pestFilterList.options.length = 1; 
+		allPests.sort(sortOrganisms);
+		for(var i in allPests)
+		{
+			var pestOpt = new Option(getLocalizedOrganismName(allPests[i]), allPests[i].organismId);
+			pestFilterList.options[pestFilterList.options.length] = pestOpt;
+		}
+		
+	}
+
+	function filterOnCropGroup(cropGroupFilterList)
+	{
+		var selectedCropGroupId = parseInt(cropGroupFilterList.options[cropGroupFilterList.options.selectedIndex].value);
+	}
+	
+	function filterObservations()
+	{
+		var pestFilterList = document.getElementById("pestFilterList");
+		var cropCategoryFilterList = document.getElementById("cropCategoryFilterList");
+		
+		var selectedPestOrganismId = parseInt(pestFilterList.options[pestFilterList.options.selectedIndex].value);
+		
+		var selectedCropCategoryIds = getMultipleSelectedValues(cropCategoryFilterList);
+		//console.info(selectedCropCategoryIds);
+		
+		var filteredObservations = [];
+		
+		for(var i=0;i<allObservations.length;i++)
+		{
+			var obs = allObservations[i];
+		
+			var validCropCategory = false;
+			if(selectedCropCategoryIds.length > 0)
+			{			
+				for(var j=0;j<selectedCropCategoryIds.length;j++)
+				{
+					if(cropCategories[selectedCropCategoryIds[j]].indexOf(obs.cropOrganism.organismId) >= 0)
+					{
+						validCropCategory = true;
+					}
+				} 
+			}
+			else
+			{
+				validCropCategory = true;
+			}
+			
+			var validPest = false;
+			if(selectedPestOrganismId == -1)
+			{
+				validPest = true;
+			}
+			else if(obs.organism.organismId == selectedPestOrganismId)
+			{
+				validPest = true;
+			}
+			
+			if(validCropCategory && validPest)
+			{
+				filteredObservations.push(obs);
+			}
+		}
+		
+		renderObservations(filteredObservations);
+	}
+	
+</script>
+{% endblock %}
\ No newline at end of file
diff --git a/observations/urls.py b/observations/urls.py
index a5ed6980567fc6162522fb888ca3f803a4bff6b2..3293b90066219351ab1d582580b9fad222f02df5 100755
--- a/observations/urls.py
+++ b/observations/urls.py
@@ -23,7 +23,7 @@ from observations import views
 urlpatterns = patterns('observations.views',
     # ex: /forecasts/                   
     url(r'^$', views.index, name='index'),
-    #url(r'new', views.index_new, name='index_new'),
+    url(r'old', views.index_old, name='index_old'),
     # ex: /observations/5/
     url(r'^(?P<observation_id>\d+)/$', (views.detail), name='detail'),
     
diff --git a/observations/views.py b/observations/views.py
index d82f3a7ca4f21e17f2d0479d38b575c3772bf809..3b5e60e615f90aa511b491a10aed7056af5946f2 100755
--- a/observations/views.py
+++ b/observations/views.py
@@ -23,8 +23,10 @@ from django.utils import translation
 from django.conf import settings
 from observations.forms import ObservationTimeFilterForm
 from organisms.models import CropCategory
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
 
-def index(request):
+def index_old(request):
     form = ObservationTimeFilterForm(request.GET)
     if form == None:
         form = ObservationTimeFilterForm() 
@@ -32,28 +34,37 @@ def index(request):
               "crop_categories": CropCategory.get_crop_categories(translation.get_language()),
               "form" : form
                }
-    return render(request, 'observations/index.html', context)
+    return render(request, 'observations/index_old.html', context)
 
-"""
-Bør slettes hvis ikke man skal ha et offentlig, spesialisert observasjonskart tilgjengelig
-def index_new(request):
+def index(request):
     # organizationId (internally - either from logged in user or from web page
     if request.session.get("vips_logic_user", None) != None:
-        print request.session["vips_logic_user"]
         organization_id = request.session["vips_logic_user"]["organization_id"]
     else:
         organization_id = settings.VIPS_ORGANIZATION_ID
-    # pestId
-    # cropId
+    
+    date_from = datetime.strptime(request.GET.get("from", "%s-01-01" % (datetime.now() + relativedelta(months = settings.SYSTEM_TIME_OFFSET_MONTHS)).year),"%Y-%m-%d")
+    if request.GET.get("to", None) is not None:
+        date_to = datetime.strptime(request.GET["to"], "%Y-%m-%d")
+    else:
+        date_to = datetime.now() + relativedelta(months = settings.SYSTEM_TIME_OFFSET_MONTHS)
+    #date_to = datetime.strptime(request.GET.get("to", "%s-12-31" % (datetime.now() + relativedelta(months = settings.SYSTEM_TIME_OFFSET_MONTHS)).year),"%Y-%m-%d")
+    period_days = date_to - date_from
+    current_day_in_period = (datetime.now() + relativedelta(months = settings.SYSTEM_TIME_OFFSET_MONTHS)) - date_from
     # cropCategoryId
-    # from (YYYY-mm-DD)
-    # to (YYYY-mm-DD)
     context = {
-              "crop_categories": CropCategory.get_crop_categories(translation.get_language()),
-              "organization_id": organization_id
-               }
-    return render(request, 'observations/index_new.html', context)
-"""
+            "from" : datetime.strftime(date_from,"%Y-%m-%d"),
+            "to" : datetime.strftime(date_to,"%Y-%m-%d"),
+            "period_days" : period_days.days + 1,
+            "current_day_in_period" : current_day_in_period,
+            "pest_id" : request.GET.get("pestId", None),
+            "crop_id" : request.GET.get("cropId", None),
+            "crop_category_id": request.GET.get("cropCategoryId", None),
+            "crop_categories": CropCategory.get_crop_categories(translation.get_language()),
+            "organization_id": organization_id
+            }
+    return render(request, 'observations/index.html', context)
+
 def detail(request, observation_id):
     context = {
               "observation_id" : observation_id,