diff --git a/src/main/webapp/templates/observationForm.ftl b/src/main/webapp/templates/observationForm.ftl
index 6fb824d1cf636baf17a3e6427a56f0d26518308a..ebba027558ab893a96a127e15f5126b7b384f6d6 100755
--- a/src/main/webapp/templates/observationForm.ftl
+++ b/src/main/webapp/templates/observationForm.ftl
@@ -47,7 +47,6 @@
 
         $(document).ready(function () {
 
-
             // Make sure that there is a datetime picker present for HTML5
             // date input fields
 
@@ -67,7 +66,7 @@
             // Otherwise, center and zoom to organizations's default
             <#if observation.location?has_content>
             initGisInfoMap([${(observation.location.x?c)!""}, ${(observation.location.y?c)!""}], 10, true);
-            <#else>
+            <#elseif !observation.observationTimeSeries?has_content>
             var geoInfo = <#if observation.geoinfo?has_content>${observation.geoinfo}<#else>
             {
             }
@@ -87,6 +86,10 @@
             initObservationData(${observation.organism.organismId}, organizationId);
             </#if>
 
+            <#if observation.observationTimeSeries?has_content>
+            displayObservationTimeSeriesInfo()
+            </#if>
+
             // Activating file selection
             $('.btn-file :file').on('fileselect', function (event, numFiles, label) {
 
@@ -136,6 +139,20 @@
             });
         }
 
+        /**
+         * Crop, pest and location should not be editable for observations belonging to time series. We avoid displaying
+         * form elements in these cases, and rather display the values statically.
+         */
+        function displayObservationTimeSeriesInfo() {
+            document.getElementById("cropDisplayName").innerHTML = nameForCropOrganismId("${observation.cropOrganismId}");
+            document.getElementById("pestDisplayName").innerHTML = nameForOrganismId("${observation.organismId}");
+            fetchPOIs(function (allPois) {
+                const locationName = nameForLocationPointOfInterestId("${observation.locationPointOfInterestId}", allPois);
+                document.getElementById("locationDisplayName").innerHTML = locationName || null;
+            });
+            initLocationMap("${observation.locationPointOfInterestId}")
+        }
+
         function getDataSchema(organismId, organizationId) {
             $.getJSON("/rest/observationdata/schema/" + organizationId + "/" + organismId, buildForm);
         }
@@ -223,6 +240,32 @@
 
         var allPois = [];
 
+
+        function fetchPOIs(callback) {
+            $.getJSON("/rest/poi/user", function (json) {
+                callback(json);
+            });
+        }
+
+        function buildPOIList(poiListElement, allPois, poiTypes, selectedPointOfInterestId) {
+            poiListElement.options.length = 1;
+            for (const [typeId, typeName] of Object.entries(poiTypes)) {
+                poiListElement.options[poiListElement.options.length] = new Option("-- " + typeName + " --", "-1");
+
+                for (let i = 0; i < allPois.length; i++) {
+                    const poi = allPois[i];
+
+                    if (poi.pointOfInterestTypeId == typeId) {
+                        const poiOption = new Option(poi.name, poi.pointOfInterestId);
+                        if (poi.pointOfInterestId === selectedPointOfInterestId) {
+                            poiOption.selected = true;
+                        }
+                        poiListElement.options[poiListElement.options.length] = poiOption;
+                    }
+                }
+            }
+        }
+
         /**
          * Fetches locations, farms, fields, traps and weather stations, renders the list
          */
@@ -234,32 +277,17 @@
                 3: "${i18nBundle.fields}",
                 5: "${i18nBundle.traps}"
             };
-            $.getJSON("/rest/poi/user", function (json) {
-                const allPois = json;
-                const poiListElement = document.getElementById("locationPointOfInterestId");
-                poiListElement.options.length = 1;
-                // Iterate through each point of interest type
-                for (const [typeId, typeName] of Object.entries(poiTypes)) {
-                    poiListElement.options[poiListElement.options.length] = new Option("-- " + typeName + " --", "-1");
-
-                    // Iterate through all POIs and add them to the list if they match the type
-                    for (let i = 0; i < allPois.length; i++) {
-                        const poi = allPois[i];
-
-                        if (poi.pointOfInterestTypeId == typeId) {
-                            const poiOption = new Option(poi.name, poi.pointOfInterestId);
-                            if (poi.pointOfInterestId === selectedPointOfInterestId) {
-                                poiOption.selected = true;
-                            }
-                            poiListElement.options[poiListElement.options.length] = poiOption;
-                        }
-                    }
-                }
+            const poiListElement = document.getElementById("locationPointOfInterestId");
+            if (!poiListElement) {
+                return;
+            }
+
+            fetchPOIs(function (allPois) {
+                buildPOIList(poiListElement, allPois, poiTypes, selectedPointOfInterestId);
                 showCorrectMap();
             });
         }
 
-
         // If locationPointOfInterestId is selected OR observation drawing map is deliberately disabled,
         // show map for locations
         // If not, show the observation drawing map
@@ -268,14 +296,17 @@
         function showCorrectMap() {
             var locationList = document.getElementById("locationPointOfInterestId");
             if (hideObservationFormMap && locationList.options[locationList.options.selectedIndex].value == "-1") {
+
                 document.getElementById("observationFormMap").style.display = "none";
                 document.getElementById("poiFormMap").style.display = "block";
                 initLocationMap(null);
             } else if (locationList.options[locationList.options.selectedIndex].value != "-1") {
+
                 document.getElementById("observationFormMap").style.display = "none";
                 document.getElementById("poiFormMap").style.display = "block";
                 initLocationMap(locationList.options[locationList.options.selectedIndex].value);
             } else {
+
                 document.getElementById("observationFormMap").style.display = "block";
                 document.getElementById("poiFormMap").style.display = "none";
             }
@@ -337,6 +368,24 @@
             }
         }
 
+        // Return name for given cropOrganismId
+        function nameForCropOrganismId(cropOrganismId) {
+            const crop = cropList.find(crop => crop.organismId == cropOrganismId);
+            return crop ? crop.displayName : null;
+        }
+
+        // Return name for given organismId
+        function nameForOrganismId(organismId) {
+            const pest = organismList.find(pest => pest.organismId == organismId);
+            return pest ? pest.displayName : null;
+        }
+
+        // Return name for given point of interest id
+        function nameForLocationPointOfInterestId(pointOfInterestId, allPois) {
+            const location = allPois.find(loc => loc.pointOfInterestId == pointOfInterestId);
+            return location ? location.name : null;
+        }
+
         /**
          * Based on the selected crop category: Put the related crops
          * on top of the crops list
@@ -386,6 +435,10 @@
 
         function renderCropList(matchingCropOrganismOptions, theRest) {
             var cropOrganismIdList = document.getElementById("cropOrganismIdList");
+            if (!cropOrganismIdList) {
+                // HTML element does not exist for observations in time series
+                return;
+            }
             cropOrganismIdList.options.length = 1;
             for (var i in matchingCropOrganismOptions) {
                 cropOrganismIdList.options[cropOrganismIdList.options.length] = matchingCropOrganismOptions[i];
@@ -432,6 +485,15 @@
             </#if>
         ];
 
+        var organismList = [
+            <#list allPests as organism>
+            {
+                organismId: ${organism.organismId},
+                displayName: "${organism.getLocalName(currentLocale.language)!""} (${organism.latinName!""})"
+            },
+            </#list>
+        ];
+
         /**
          * Does all the ifs and buts before form can potentially be submitted
          */
@@ -497,102 +559,128 @@
                                 : ${observation.lastEditedByUser.firstName} ${observation.lastEditedByUser.lastName}</label>
                         </div>
                     </#if>
-                    <#if ! observation.organism?has_content>
+                    <#if observation.observationTimeSeries?has_content>
+                        <div class="form-group">
+                            <label>Tidsserie: ${observation.observationTimeSeries.name}</label><br>
+                            <i>Tidsserier opprettes via appen VIPS feltobservasjoner. Kultur, organisme og sted er
+                                felles for alle observasjoner innenfor en tidsserie, og kan derfor ikke redigeres per
+                                observasjon.</i>
+                        </div>
+                        <div class="form-group">
+                            <label for="cropOrganismId">${i18nBundle.cropOrganismId}</label>
+                            <span id="cropDisplayName"></span>
+                        </div>
+                        <div class="form-group">
+                            <label for="organismId">${i18nBundle.organism}</label>
+                            <span id="pestDisplayName"></span>
+                        </div>
+                        <div class="form-group">
+                            <label for="locationPointOfInterestId">${i18nBundle.location}</label>
+                            <span id="locationDisplayName"></span>
+                        </div>
+                        <input type="hidden" name="cropOrganismId" value="${observation.cropOrganism.organismId}">
+                        <input type="hidden" name="organismId" value="${observation.organism.organismId}">
+                        <input type="hidden" name="locationPointOfInterestId"
+                               value="${observation.locationPointOfInterestId}">
+                        <input type="hidden" name="locationVisibility" value="${locationVisibilityFormValue}">
+                    <#else>
+                        <#if ! observation.organism?has_content>
+                            <div class="form-group">
+                                <label for="cropCategoryId">${i18nBundle.listSelectedCropCategoryOnTop}</label>
+                                <select class="form-control" id="cropCategoryIdList" name="cropCategoryId"
+                                        onchange="filterCrops(this.options[this.options.selectedIndex].value);">
+                                    <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.cropCategory?lower_case}</option>
+                                    <!-- Options added by JavaScript function renderCropCategories() -->
+                                </select>
+                            </div>
+                        </#if>
                         <div class="form-group">
-                            <label for="cropCategoryId">${i18nBundle.listSelectedCropCategoryOnTop}</label>
-                            <select class="form-control" id="cropCategoryIdList" name="cropCategoryId"
-                                    onchange="filterCrops(this.options[this.options.selectedIndex].value);">
-                                <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.cropCategory?lower_case}</option>
-                                <!-- Options added by JavaScript function renderCropCategories() -->
+                            <label for="cropOrganismId">${i18nBundle.cropOrganismId}</label>
+                            <select class="form-control" id="cropOrganismIdList" name="cropOrganismId"
+                                    <#if observation.observationId?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly" <#else> onblur="validateField(this);" onchange="updateCropPests();"</#if>>
+                                <#if ! observation.observationId?has_content || user.isSuperUser() || user.isOrganizationAdmin()>
+                                    <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.cropOrganismId?lower_case}</option>
+                                    <option value="-10"
+                                            <#if (observation.cropOrganism?has_content && observation.cropOrganism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option>
+                                </#if>
                             </select>
+                            <span class="help-block" id="${formId}_cropOrganismId_validation"></span>
                         </div>
-                    </#if>
-                    <div class="form-group">
-                        <label for="cropOrganismId">${i18nBundle.cropOrganismId}</label>
-                        <select class="form-control" id="cropOrganismIdList" name="cropOrganismId"
-                                <#if observation.observationId?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly" <#else> onblur="validateField(this);" onchange="updateCropPests();"</#if>>
-                            <#if ! observation.observationId?has_content || user.isSuperUser() || user.isOrganizationAdmin()>
-                                <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.cropOrganismId?lower_case}</option>
-                                <option value="-10"
-                                        <#if (observation.cropOrganism?has_content && observation.cropOrganism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option>
-                            </#if>
-                        </select>
-                        <span class="help-block" id="${formId}_cropOrganismId_validation"></span>
-                    </div>
-                    <div class="form-group">
-                        <label for="organismId">${i18nBundle.organism}</label>
-                        <select class="form-control" name="organismId"
-                                <#if observation.organism?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly"
-                                <#else>onchange="<#if noBroadcast>updateHeadingAndText(this.options[this.options.selectedIndex].text);</#if>initObservationData(this.options[this.options.selectedIndex].value,organizationId);"
-                                onblur="validateField(this);"</#if>>
-                            <#if ! observation.organism?has_content || user.isSuperUser() || user.isOrganizationAdmin()>
-                                <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.organism?lower_case}</option>
-                                <option value="-10"
-                                        <#if (observation.organism?has_content && observation.organism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option>
-                                <#list allPests as organism>
-                                    <option value="${organism.organismId}"
-                                            <#if (observation.organism?has_content && observation.organism.organismId == organism.organismId)>selected="selected"</#if>
-                                    >${organism.getLocalName(currentLocale.language)!""} (${organism.latinName!""}
-                                        ) ${hierarchyCategories.getName(organism.hierarchyCategoryId)?upper_case}</option>
-                                </#list>
-                            <#else>
-                                <#list allPests as organism>
-                                    <#if (observation.organism?has_content && observation.organism.organismId == organism.organismId)>
+                        <div class="form-group">
+                            <label for="organismId">${i18nBundle.organism}</label>
+                            <select class="form-control" name="organismId"
+                                    <#if observation.organism?has_content && ! user.isSuperUser() && ! user.isOrganizationAdmin()>readonly="readonly"
+                                    <#else>onchange="<#if noBroadcast>updateHeadingAndText(this.options[this.options.selectedIndex].text);</#if>initObservationData(this.options[this.options.selectedIndex].value,organizationId);"
+                                    onblur="validateField(this);"</#if>>
+                                <#if !observation.organism?has_content || user.isSuperUser() || user.isOrganizationAdmin()>
+                                    <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.organism?lower_case}</option>
+                                    <option value="-10"
+                                            <#if (observation.organism?has_content && observation.organism.organismId == -10)>selected="selected"</#if>>${i18nBundle.missingInDatabase}</option>
+                                    <#list allPests as organism>
                                         <option value="${organism.organismId}"
-                                                selected="selected">${organism.getLocalName(currentLocale.language)!""}
-                                            (${organism.latinName!""}
+                                                <#if (observation.organism?has_content && observation.organism.organismId == organism.organismId)>selected="selected"</#if>
+                                        >${organism.getLocalName(currentLocale.language)!""} (${organism.latinName!""}
                                             ) ${hierarchyCategories.getName(organism.hierarchyCategoryId)?upper_case}</option>
-                                    </#if>
-                                </#list>
-                            </#if>
-                        </select>
-                        <span class="help-block" id="${formId}_organismId_validation"></span>
+                                    </#list>
+                                <#else>
+                                    <#list allPests as organism>
+                                        <#if (observation.organism?has_content && observation.organism.organismId == organism.organismId)>
+                                            <option value="${organism.organismId}"
+                                                    selected="selected">${organism.getLocalName(currentLocale.language)!""}
+                                                (${organism.latinName!""}
+                                                ) ${hierarchyCategories.getName(organism.hierarchyCategoryId)?upper_case}</option>
+                                        </#if>
+                                    </#list>
+                                </#if>
+                            </select>
+                            <span class="help-block" id="${formId}_organismId_validation"></span>
 
-                    </div>
-                    <div class="form-group">
-                        <label for="locationPointOfInterestId">${i18nBundle.location}&nbsp;&nbsp;<button role="button"
-                                                                                                         type="button"
-                                                                                                         onclick="addNewLocationPopup();">${i18nBundle.addNew}</button>
-                        </label>
-                        <select class="form-control" name="locationPointOfInterestId" id="locationPointOfInterestId"
-                                onchange="showCorrectMap();" <#if editAccess!="W">readonly="readonly"</#if>>
-                            <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.location?lower_case}</option>
-                        </select>
-                        <span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span>
-                    </div>
-                    <#if editAccess!="W" && observation.locationIsPrivate?has_content && observation.locationIsPrivate == true>
-                        <input type="hidden" name="locationVisibility" value="private"/>
-                    <#else>
+                        </div>
                         <div class="form-group">
-                            <div class="radio">
-                                <label>
-                                    <input type="radio" name="locationVisibility" value="public"
-                                           <#if locationVisibilityFormValue == "public">checked="checked"</#if>
-                                    />
-                                </label>
-                                ${i18nBundle.locationIsPublic}
-                            </div>
-                            <div class="radio">
-
-                                <label>
-                                    <input type="radio" name="locationVisibility" value="private"
-                                           <#if locationVisibilityFormValue == "private">checked="checked"</#if>
-                                    />
-                                </label>
-                                ${i18nBundle.locationIsPrivate}
-                            </div>
-                            <#list polygonServices as polygonService>
+                            <label for="locationPointOfInterestId">${i18nBundle.location}&nbsp;&nbsp;<button
+                                        role="button" type="button"
+                                        onclick="addNewLocationPopup();">${i18nBundle.addNew}</button>
+                            </label>
+                            <select class="form-control" name="locationPointOfInterestId" id="locationPointOfInterestId"
+                                    onchange="showCorrectMap();" <#if editAccess!="W">readonly="readonly"</#if>>
+                                <option value="-1">${i18nBundle.pleaseSelect} ${i18nBundle.location?lower_case}</option>
+                            </select>
+                            <span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span>
+                        </div>
+                        <#if editAccess!="W" && observation.locationIsPrivate?has_content && observation.locationIsPrivate == true>
+                            <input type="hidden" name="locationVisibility" value="private"/>
+                        <#else>
+                            <div class="form-group">
                                 <div class="radio">
                                     <label>
-                                        <input type="radio" name="locationVisibility"
-                                               value="mask_${polygonService.polygonServiceId}"
-                                               <#if locationVisibilityFormValue == "mask_" + polygonService.polygonServiceId>checked="checked"</#if>
+                                        <input type="radio" name="locationVisibility" value="public"
+                                               <#if locationVisibilityFormValue == "public">checked="checked"</#if>
                                         />
-                                        ${i18nBundle.maskObservationWith} ${polygonService.polygonServiceName}
                                     </label>
+                                    ${i18nBundle.locationIsPublic}
                                 </div>
-                            </#list>
-                        </div>
+                                <div class="radio">
+
+                                    <label>
+                                        <input type="radio" name="locationVisibility" value="private"
+                                               <#if locationVisibilityFormValue == "private">checked="checked"</#if>
+                                        />
+                                    </label>
+                                    ${i18nBundle.locationIsPrivate}
+                                </div>
+                                <#list polygonServices as polygonService>
+                                    <div class="radio">
+                                        <label>
+                                            <input type="radio" name="locationVisibility"
+                                                   value="mask_${polygonService.polygonServiceId}"
+                                                   <#if locationVisibilityFormValue == "mask_" + polygonService.polygonServiceId>checked="checked"</#if>
+                                            />
+                                            ${i18nBundle.maskObservationWith} ${polygonService.polygonServiceName}
+                                        </label>
+                                    </div>
+                                </#list>
+                            </div>
+                        </#if>
                     </#if>
                     <div class="form-group">
                         <label for="organizationGroupId">${i18nBundle.availableFor} ${i18nBundle.organizationGroupList?lower_case}</label>