Skip to content
Snippets Groups Projects
Commit 49dfd273 authored by Lene Wasskog's avatar Lene Wasskog
Browse files

feat: New point saved to db

Selection of existing points in map updates the dropdown
parent 21b2ddd1
Branches
No related tags found
1 merge request!191Add map module and Open-Meteo support
......@@ -18,6 +18,7 @@
package no.nibio.vips.logic.controller.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibm.icu.util.Calendar;
import java.io.IOException;
import java.text.ParseException;
......@@ -37,12 +38,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import no.nibio.vips.logic.controller.session.ForecastBean;
import no.nibio.vips.logic.controller.session.OrganismBean;
import no.nibio.vips.logic.controller.session.PointOfInterestBean;
import no.nibio.vips.logic.controller.session.UserBean;
import no.nibio.vips.logic.entity.ForecastConfiguration;
import no.nibio.vips.logic.entity.ModelInformation;
import no.nibio.vips.logic.entity.Organization;
import no.nibio.vips.logic.entity.VipsLogicRole;
import no.nibio.vips.logic.entity.VipsLogicUser;
import no.nibio.vips.logic.entity.*;
import no.nibio.vips.logic.i18n.SessionLocaleUtil;
import no.nibio.vips.logic.util.Globals;
import no.nibio.vips.logic.util.SystemTime;
......@@ -52,6 +50,9 @@ import no.nibio.web.forms.FormField;
import no.nibio.web.forms.FormValidation;
import no.nibio.web.forms.FormValidationException;
import no.nibio.web.forms.FormValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wololo.geojson.GeoJSON;
/**
* Handles form configuration actions
......@@ -59,7 +60,7 @@ import no.nibio.web.forms.FormValidator;
* @author Tor-Einar Skog <tor-einar.skog@nibio.no>
*/
public class ForecastConfigurationController extends HttpServlet {
@PersistenceContext(unitName="VIPSLogic-PU")
EntityManager em;
......@@ -72,6 +73,9 @@ public class ForecastConfigurationController extends HttpServlet {
@EJB
OrganismBean organismBean;
@EJB
PointOfInterestBean pointOfInterestBean;
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code>
* methods.
......@@ -203,7 +207,7 @@ public class ForecastConfigurationController extends HttpServlet {
if(forecastConfiguration == null)
{
forecastConfiguration = new ForecastConfiguration();
multipleNew = request.getParameter("multipleNew") != null
multipleNew = request.getParameter("multipleNew") != null
&& request.getParameter("multipleNew").equals("true");
}
// Only superusers can view and edit forecasts from other organizations
......@@ -221,14 +225,22 @@ public class ForecastConfigurationController extends HttpServlet {
// TODO: More intelligent selection of locations, weather stations and users
if(user.isSuperUser())
{
request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findAll").getResultList());
request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findAllByActivity").setParameter("active", Boolean.TRUE).getResultList());
List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findAll").getResultList();
request.setAttribute("locationPointOfInterests", poiList);
request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findAllByActivity").setParameter("active", Boolean.TRUE).getResultList();
request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findAll").getResultList());
}
else
{
request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList());
List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList();
request.setAttribute("locationPointOfInterests", poiList);
request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList();
request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
}
request.setAttribute("forecastConfiguration", forecastConfiguration);
......@@ -244,7 +256,7 @@ public class ForecastConfigurationController extends HttpServlet {
request.setAttribute("modelInformations", forecastBean.getBatchableModels());
request.setAttribute("messageKey", request.getParameter("messageKey"));
request.getRequestDispatcher("/forecastConfigurationForm.ftl").forward(request, response);
}
}
catch(NullPointerException | NumberFormatException ex)
......@@ -264,13 +276,17 @@ public class ForecastConfigurationController extends HttpServlet {
forecastConfiguration.setIsPrivate(Boolean.TRUE);
}
request.setAttribute("forecastConfiguration", forecastConfiguration);
request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList());
request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList());
List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findByOrganizationId").setParameter("organizationId", user.getOrganizationId()).getResultList();
request.setAttribute("locationPointOfInterests", poiList);
request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findByActivityAndOrganizationId").setParameter("active", Boolean.TRUE).setParameter("organizationId", user.getOrganizationId()).getResultList();
request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
request.setAttribute("forecastConfiguration", forecastConfiguration);
request.setAttribute("formId","forecastConfigurationForm");
request.getSession().setAttribute("availableTimeZones", SystemTime.getAvailableTimeZones());
request.getSession().setAttribute("defaultTimeZoneId", user.getOrganizationId().getDefaultTimeZone());
request.setAttribute("allCrops", em.createNamedQuery("Organism.findAllCrops").getResultList());
request.setAttribute("allPests", em.createNamedQuery("Organism.findAllPests").getResultList());
// Hierarchy categories
......@@ -279,13 +295,13 @@ public class ForecastConfigurationController extends HttpServlet {
request.setAttribute("messageKey", request.getParameter("messageKey"));
request.getRequestDispatcher("/forecastConfigurationForm.ftl").forward(request, response);
}
else
else
{
response.sendError(403,"Access not authorized");
}
}
}
// Store forecast configuration(s)
// Authorization: SUPERUSERS and ORGANIZATION ADMINS
else if(action.equals("forecastConfigurationFormSubmit"))
......@@ -310,8 +326,8 @@ public class ForecastConfigurationController extends HttpServlet {
}
else
{
String formId = request.getParameter("multipleNew") != null && request.getParameter("multipleNew").equals("true") ?
"forecastConfigurationMultipleNewForm"
String formId = request.getParameter("multipleNew") != null && request.getParameter("multipleNew").equals("true") ?
"forecastConfigurationMultipleNewForm"
:"forecastConfigurationForm";
FormValidation formValidation = FormValidator.validateForm(formId,request,getServletContext());
// Also validation the model specific fields
......@@ -344,7 +360,7 @@ public class ForecastConfigurationController extends HttpServlet {
for(String optionVal:formValidation.getFormField("weatherStationPointOfInterestIds").getWebValues())
{
Integer weatherStationPointOfInterestId = Integer.valueOf(optionVal);
forecastBean.storeNewMultipleForecastConfiguration(weatherStationPointOfInterestId, formValidation.getFormFields(), modelFieldFormValidation.getFormFields());
forecastBean.storeNewMultipleForecastConfiguration(weatherStationPointOfInterestId, formValidation.getFormFields(), modelFieldFormValidation.getFormFields());
}
request.setAttribute("messageKey", request.getParameter("multipleForecastConfigurationsCreated"));
response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?messageKey=").append("multipleForecastConfigurationsCreated").toString());
......@@ -357,8 +373,12 @@ public class ForecastConfigurationController extends HttpServlet {
// We must get date formats!
Map<String, FormField> formFields = FormValidator.getFormFields("forecastConfigurationForm",getServletContext());
// TODO: More intelligent selection of locations, weather stations and users
request.setAttribute("locationPointOfInterests", em.createNamedQuery("PointOfInterest.findAll").getResultList());
request.setAttribute("weatherStationPointOfInterests", em.createNamedQuery("PointOfInterestWeatherStation.findAll").getResultList());
List<PointOfInterest> poiList = em.createNamedQuery("PointOfInterest.findAll").getResultList();
request.setAttribute("locationPointOfInterests", poiList);
request.setAttribute("locationPointOfInterestsGeoJson", convertToGeoJson(poiList));
List<PointOfInterest> weatherStationPoiList = em.createNamedQuery("PointOfInterestWeatherStation.findAll").getResultList();
request.setAttribute("weatherStationPointOfInterests", weatherStationPoiList);
request.setAttribute("weatherStationPointOfInterestsGeoJson", convertToGeoJson(weatherStationPoiList));
request.setAttribute("vipsLogicUsers", em.createNamedQuery("VipsLogicUser.findAll").getResultList());
request.setAttribute("dateStart_dateFormat", formFields.get("dateStart").getDateFormat());
request.setAttribute("dateEnd_dateFormat", formFields.get("dateEnd").getDateFormat());
......@@ -403,13 +423,13 @@ public class ForecastConfigurationController extends HttpServlet {
try
{
forecastBean.deleteForecastConfiguration(forecastConfigurationId);
response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?").append("&messageKey=").append("forecastConfigurationDeleted").toString());
response.sendRedirect(new StringBuilder(Globals.PROTOCOL + "://").append(ServletUtil.getServerName(request)).append("/forecastConfiguration?").append("&messageKey=").append("forecastConfigurationDeleted").toString());
}
catch(NullPointerException | NumberFormatException ex)
{
response.sendError(500, "Invalid forecast configurationId " + request.getParameter("forecastConfigurationId"));
}
}
else
{
......@@ -418,6 +438,11 @@ public class ForecastConfigurationController extends HttpServlet {
}
}
private GeoJSON convertToGeoJson(List<PointOfInterest> poiList) {
return pointOfInterestBean.getPoisAsGeoJson(poiList);
}
// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
......
......@@ -37,7 +37,9 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.fasterxml.jackson.core.JsonProcessingException;
import no.nibio.vips.logic.controller.session.PointOfInterestBean;
import no.nibio.vips.logic.entity.helpers.PointOfInterestFactory;
import org.jboss.resteasy.spi.HttpRequest;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
......@@ -102,7 +104,70 @@ public class POIService {
PointOfInterest retVal = SessionControllerGetter.getPointOfInterestBean().getPointOfInterest(pointOfInterestId);
return Response.ok().entity(retVal).build();
}
@POST
@Consumes("application/json;charset=UTF-8")
@Produces("application/json;charset=UTF-8")
public Response postPoi(String poiJson) {
// TODO Fix authentication
ObjectMapper oM = new ObjectMapper();
Map<Object, Object> poiMap;
try {
poiMap = oM.readValue(poiJson, new TypeReference<HashMap<Object, Object>>() {});
} catch (JsonProcessingException e) {
LOGGER.error(e.getMessage(), e);
return Response.status(Status.BAD_REQUEST).entity("Unable to parse input").build();
}
Integer poiUserId = poiMap.get("userId") != null ? Integer.parseInt(poiMap.get("userId").toString()) : null;
VipsLogicUser user = SessionControllerGetter.getUserBean().getVipsLogicUser(poiUserId);
if (user == null) {
LOGGER.error("No user found for userId={}", poiUserId);
return Response.status(Status.UNAUTHORIZED).build();
}
LOGGER.error("Remember to check for roles as well, if necessary!");
PointOfInterestBean poiBean = SessionControllerGetter.getPointOfInterestBean();
Integer poiTypeId = poiMap.get("typeId") != null ? Integer.parseInt(poiMap.get("typeId").toString()) : null;
if(poiTypeId == null) {
return Response.status(Status.BAD_REQUEST).entity("Point of interest type is required").build();
}
String poiName = poiMap.get("name") != null ? poiMap.get("name").toString() : null;
Double poiLongitude = poiMap.get("longitude") != null ? Double.valueOf(poiMap.get("longitude").toString()): null;
Double poiLatitude = poiMap.get("latitude") != null ? Double.valueOf(poiMap.get("latitude").toString()): null;
Double poiAltitude = poiMap.get("altitude") != null ? Double.valueOf(poiMap.get("altitude").toString()): null;
PointOfInterest poiToSave = PointOfInterestFactory.getPointOfInterest(poiTypeId);
poiToSave.setName(poiName);
poiToSave.setLongitude(poiLongitude);
poiToSave.setLatitude(poiLatitude);
poiToSave.setAltitude(poiAltitude);
poiToSave.setLastEditedTime(new Date());
poiToSave.setUser(user);
poiToSave.setCountryCode(user.getOrganizationId().getCountryCode());
if (poiLongitude != null && poiLatitude != null && poiAltitude != null) {
GISUtil gisUtil = new GISUtil();
Coordinate coordinate = new Coordinate(poiLongitude, poiLatitude, poiAltitude);
Point p3d = gisUtil.createPointWGS84(coordinate);
poiToSave.setGisGeom(p3d);
}
poiToSave = poiBean.storePoi(poiToSave);
if (poiToSave != null) {
return Response.status(Response.Status.CREATED)
.entity(poiToSave)
.build();
} else {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Failed to create PointOfInterest")
.build();
}
}
/**
* @param organizationId Id of the organization in question
* @param poiTypesStr Comma separated list of poiTypes
......
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: rgba(0, 0, 0, 0.9);
}
.modal-content {
position: relative;
height: 100%;
width: 100%;
background-color: #fefefe;
}
#selectedPointInfo {
font-family: Arial, sans-serif;
font-size: 12px;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
width: 150px;
position: absolute;
bottom: 20px;
left: 20px;
background: white;
padding: 10px;
border-radius: 5px;
z-index: 1100;
}
#confirmButton {
margin-top: 10px;
}
.leaflet-container {
cursor: default !important; /* Force the arrow cursor */
}
.leaflet-container {
font-size: 0.9rem; /* Make font-size of popup larger */
}
#pointForm {
z-index: 1200;
position: absolute;
}
#poiPopup {
width: 100px;
}
\ No newline at end of file
// mapModal.js
import {
map,
tileLayer,
marker,
geoJSON,
circleMarker,
DomEvent
} from 'https://unpkg.com/leaflet/dist/leaflet-src.esm.js';
/**
* Uses css classes from bootstrap 3.4.1
*
*/
class MapModal {
constructor(mapContainerId, typeNameMap, geoJsonData, allowNewPoints = false, callback = null) {
this.mapContainerId = mapContainerId;
this.typeNameMap = typeNameMap;
this.geojsonData = geoJsonData;
this.allowNewPoints = allowNewPoints;
this.map = null;
this.isMapInitialized = false;
this.selectedPointLayer = null;
this.createdPointLayer = null;
this.createdPoints = [];
this.callback = callback;
this.typeColorMap = {
0: "#B3CDE0", // Light blue
1: "#FBB4AE", // Soft pink
2: "#CCEBC5", // Light green
3: "#DECBE4", // Lavender
5: "#FED9A6", // Light peach
6: "#FFFFCC", // Pale yellow
7: "#E5D8BD" // Beige
};
}
initMap() {
if (!this.isMapInitialized) {
// Initialize the map centered on Norway
this.map = map(this.mapContainerId).setView([63.4226, 10.3951], 5);
// Add a tile layer
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19
}).addTo(this.map);
this.createSelectedPointInfo();
// Add points to the map
geoJSON(this.geojsonData, {
filter: (feature) => feature.geometry.type === "Point",
pointToLayer: (feature, latlng) => {
const color = this.typeColorMap[feature.properties.pointOfInterestTypeId] || "#3498DB";
return circleMarker(latlng, {
radius: 8, // Size of the marker
fillColor: color,
color: "#FFFFFF", // Border color
weight: 2, // Border thickness
opacity: 1,
fillOpacity: 0.8
});
},
onEachFeature: (feature, layer) => {
layer.bindPopup(this.popupContent(feature));
layer.on('click', () => this.selectPoint(feature, layer));
}
}).addTo(this.map);
// Enable adding new points if allowed
if (this.allowNewPoints) {
console.info("Enable point creation")
this.enablePointCreation();
}
this.isMapInitialized = true;
}
}
createSelectedPointInfo() {
console.debug("Create information panel for selected point, initially hidden");
const selectedPointInfoHtml = `
<div id="selectedPointInfo" style="display: none;">
<div id="infoMessage"></div>
<button id="confirmButton" class="btn btn-primary">OK</button>
</div>
` ;
document.getElementById(this.mapContainerId).insertAdjacentHTML('beforeend', selectedPointInfoHtml);
}
updateSelectedPointInfo(feature) {
const name = feature.properties.pointOfInterestName;
const type = this.typeNameMap[feature.properties.pointOfInterestTypeId];
const coordinates = feature.geometry.coordinates[1].toFixed(5) + ", " + feature.geometry.coordinates[0].toFixed(5);
const selectedPointInfo = document.getElementById('selectedPointInfo');
const infoMessage = document.getElementById('infoMessage');
infoMessage.innerHTML = `<strong>${name}</strong><br>${coordinates}<br>${type}`;
selectedPointInfo.style.display = 'block';
const confirmButton = document.getElementById('confirmButton');
confirmButton.style.display = 'block';
confirmButton.onclick = () => {
this.confirmSelection(feature);
};
}
selectPoint(feature, layer) {
// Deselect previously selected point, if any
if (this.selectedPointLayer) {
const color = this.typeColorMap[feature.properties.pointOfInterestTypeId] || "#3498DB";
this.selectedPointLayer.setStyle({
radius: 8, // Size of the marker
fillColor: color,
color: "#FFFFFF", // Border color
weight: 2, // Border thickness
opacity: 1,
fillOpacity: 0.8
});
}
// Highlight the new selected point
layer.setStyle({
fillColor: 'red',
color: '#f00'
});
this.updateSelectedPointInfo(feature)
this.selectedPointLayer = layer;
}
confirmSelection(feature) {
console.info("Save pointData", feature);
const pointData = {
id: feature.properties.pointOfInterestId,
name: feature.properties.pointOfInterestName,
typeId: feature.properties.pointOfInterestTypeId,
altitude: feature.properties.pointOfInterestAltitude,
longitude: feature.geometry.coordinates[0],
latitude: feature.geometry.coordinates[1]
};
if (typeof this.callback === 'function') {
console.info("Callback!", pointData);
this.callback(pointData);
}
this.closeModal();
}
enablePointCreation() {
this.map.on('click', (e) => {
console.log("Map clicked, enabling point creation...");
const latlng = e.latlng;
// If a form already exists, remove it
this.closeNewPointFormIfOpen();
// Calculate the pixel position from the map's click event
const containerPoint = this.map.latLngToContainerPoint(latlng);
// Show form for creating a new point
const formHtml = `
<div id="pointForm" class="panel panel-default" style="top: ${containerPoint.y}px; left: ${containerPoint.x}px;">
<div class="panel-heading">
<h3 class="panel-title">Create new point</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="form-group">
<label for="altitude">Altitude:</label>
<input type="text" class="form-control" id="altitude" name="altitude">
</div>
<div class="form-group">
<label for="poiTypeSelect">Type:</label>
<select class="form-control" id="poiTypeSelect" name="poiTypeSelect"></select>
</div>
<div class="form-group text-right">
<button id="savePointButton" class="btn btn-primary">Save</button>
</div>
</div>
</div>`;
document.getElementById(this.mapContainerId).insertAdjacentHTML('beforeend', formHtml);
const typeSelectElement = document.getElementById("poiTypeSelect");
typeSelectElement.innerHTML = '';
for (const [id, name] of Object.entries(this.typeNameMap)) {
const option = document.createElement('option');
option.value = id;
option.textContent = name;
typeSelectElement.appendChild(option);
}
const formElement = document.getElementById('pointForm');
DomEvent.disableClickPropagation(formElement);
// Add event listener to close the form if clicked outside
document.addEventListener('click', this.handleClickOutsidePointForm.bind(this), true);
document.getElementById('savePointButton').addEventListener('click', () => {
this.savePoint(latlng.lat, latlng.lng);
});
});
}
handleClickOutsidePointForm(event) {
const formElement = document.getElementById('pointForm');
// If the clicked element is not inside the form, close the form
if (formElement && !formElement.contains(event.target)) {
this.closeNewPointFormIfOpen();
}
}
closeNewPointFormIfOpen() {
const formElement = document.getElementById('pointForm');
if (formElement) {
formElement.remove();
}
// Remove the event listener after closing the form
document.removeEventListener('click', this.handleClickOutsidePointForm.bind(this), true);
}
savePoint(lat, lng) {
const nameElement = document.getElementById('name');
const altitudeElement = document.getElementById('altitude');
const poiTypeSelectElement = document.getElementById("poiTypeSelect")
if (nameElement && poiTypeSelectElement) {
const poiName = nameElement.value;
const poiAltitude = altitudeElement.value;
const poiType = poiTypeSelectElement.value;
if (this.createdPointLayer) {
this.map.removeLayer(this.createdPointLayer);
}
const newPoint = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [lng, lat]
},
"properties": {
"pointOfInterestName": poiName,
"pointOfInterestAltitude": poiAltitude,
"pointOfInterestTypeId": poiType
}
};
if (this.createdPoints.length > 0) {
this.createdPoints.pop(); // Remove the last created point from the list
}
this.createdPoints.push(newPoint);
this.createdPointLayer = geoJSON(newPoint, {
pointToLayer: (feature, latlng) => {
return circleMarker(latlng, {
radius: 8,
fillColor: 'green',
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
},
onEachFeature: (feature, layer) => {
layer.bindPopup(this.popupContent(feature));
layer.on('click', () => this.selectPoint(feature, layer));
}
}).addTo(this.map);
this.createdPointLayer.eachLayer((layer) => {
this.selectPoint(newPoint, layer);
});
const formElement = document.getElementById('pointForm');
if (formElement) {
formElement.remove();
}
}
}
popupContent(feature) {
const localizedTypeName = this.typeNameMap[feature.properties.pointOfInterestTypeId];
const coordinates = feature.geometry.coordinates[1].toFixed(5) + ", " + feature.geometry.coordinates[0].toFixed(5);
return `<div id="poiPopup">
<strong>${feature.properties.pointOfInterestName}</strong><br>${coordinates}<br>${localizedTypeName}
</div>`;
}
openModal(points) {
document.getElementById('mapModal').style.display = 'block';
this.initMap();
}
closeModal() {
document.getElementById('mapModal').style.display = 'none';
}
}
// Export the module
export default MapModal;
\ No newline at end of file
<#--
Copyright (c) 2016 NIBIO <http://www.nibio.no/>.
<#--
Copyright (c) 2016 NIBIO <http://www.nibio.no/>.
This file is part of VIPSLogic.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
......@@ -16,21 +16,78 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
--><#include "master.ftl">
<#macro page_head>
<title>${i18nBundle.viewForecastConfiguration}</title>
<title>${i18nBundle.viewForecastConfiguration}</title>
</#macro>
<#macro custom_js>
<script src="/js/resourcebundle.js"></script>
<script src="/js/forecastConfigurationForm.js"></script>
<script src="/js/validateForm.js"></script>
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<link href="//code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css" rel="stylesheet" />
<script type="text/javascript" src="/js/3rdparty/modernizr_custom.js"></script>
<script type="text/javascript" src="/js/3rdparty/moment.min.js"></script>
<script src="/js/resourcebundle.js"></script>
<script src="/js/forecastConfigurationForm.js"></script>
<script src="/js/validateForm.js"></script>
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
<link href="//code.jquery.com/ui/1.10.3/themes/redmond/jquery-ui.css" rel="stylesheet" />
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<link rel="stylesheet" href="/css/mapModal.css" />
<script type="text/javascript" src="/js/3rdparty/modernizr_custom.js"></script>
<script type="text/javascript" src="/js/3rdparty/moment.min.js"></script>
<script type="text/javascript" src="/js/environment.js"></script>
<script type="text/javascript" src="/js/util.js"></script>
<script type="text/javascript" src="/js/3rdparty/chosen.jquery.min.js"></script>
<script type="text/javascript">
$(".chosen-select").chosen();
</script>
<script type="module">
import MapModal from '/js/mapModal.js';
function callbackUpdateLocationPointOfInterest(pointData) {
const selectBox = document.querySelector('select[name="locationPointOfInterestId"]');
if(pointData.id) {
let optionFound = false;
for (let i = 0; i < selectBox.options.length; i++) {
if (selectBox.options[i].value == pointData.id) {
selectBox.selectedIndex = i; // Select the matching option
optionFound = true;
break;
}
}
if (!optionFound) {
console.error("No matching option found for feature.id:", pointData.id);
}
} else {
const userId = ${user.userId};
const params = {
'name': pointData.name,
'typeId': pointData.typeId,
'longitude': pointData.longitude,
'latitude': pointData.latitude,
'altitude': pointData.altitude,
'userId': userId,
}
$.ajax({
url: "/rest/poi",
type: "POST",
contentType: "application/json",
data: JSON.stringify(params),
success: function(response) {
console.info("Success:", response);
},
error: function(jqXHR, textStatus, errorThrown) {
console.error("Error:", textStatus, errorThrown);
}
});
}
}
const typeNameMap = {
2: "${i18nBundle["pointOfInterestType_2"]}",
3: "${i18nBundle["pointOfInterestType_3"]}",
5: "${i18nBundle["pointOfInterestType_5"]}"
};
const poiGeoJson = JSON.parse('${locationPointOfInterestsGeoJson?json_string}');
const stationGeoJson = JSON.parse('${weatherStationPointOfInterestsGeoJson?json_string}')
const mapModalInstance = new MapModal('mapContainer', typeNameMap, poiGeoJson, true, callbackUpdateLocationPointOfInterest);
window.mapModalInstance = mapModalInstance;
window.openModal = () => window.mapModalInstance && window.mapModalInstance.openModal();
window.closeModal = () => window.mapModalInstance && window.mapModalInstance.closeModal();
</script>
<script type="text/javascript">
$(document).ready(function() {
......@@ -278,6 +335,14 @@
<option value="${poi.pointOfInterestId}"<#if forecastConfiguration.locationPointOfInterestId?has_content && poi.pointOfInterestId == forecastConfiguration.locationPointOfInterestId.pointOfInterestId> selected="selected"</#if>>${poi.name}</option>
</#list>
</select>
<button onclick="openModal()">Open Map</button>
<div id="mapModal" class="modal">
<div class="modal-content">
<span class="close-button" onclick="closeModal()">&times;</span>
<div id="mapContainer" style="height: 100vh; width: 100%; position: relative;">
</div>
</div>
</div>
<span class="help-block" id="${formId}_locationPointOfInterestId_validation"></span>
</div>
<div class="form-group">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment