# TP - Jeu pervasif
> Colin Placier
> Maxime Leriche
## Mise en place
Repository du projet : <https://github.com/batleforc/geogo>
Afin d'avoir un environnement simplifié nous somme passé par un docker compose que l'on démarrera via `docker compose up -d` :
```yaml
services:
postgis:
image: postgis/postgis
container_name: postgis
ports:
- 5432:5432
volumes:
- ./data/postgis:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DBNAME=postgres
- PGDATA=/var/lib/postgresql/data
geoserver:
image: kartoza/geoserver:2.21.0
ports:
- 8081:8080
restart: on-failure
volumes:
- ./data/geoserver:/opt/geoserver/data_dir
environment:
GEOSERVER_DATA_DIR: /opt/geoserver/data_dir
GEOWEBCACHE_CACHE_DIR: /opt/geoserver/data_dir/gwc
GEOSERVER_ADMIN_PASSWORD: myawesomegeoserver
GEOSERVER_ADMIN_USER: admin
INITIAL_MEMORY: 2G
MAXIMUM_MEMORY: 4G
healthcheck:
test: curl --fail -s http://localhost:8080/ || exit 1
interval: 1m30s
timeout: 10s
retries: 3
start_period: 30s
tomcat:
image: tomcat:8.5.83-jre11-temurin-focal
ports:
- "8080:8080"
volumes:
- ./data/webapps:/usr/local/tomcat/webapps
```
Celui-ci permet de lancer 3 service:
- Postgis
- GeoServer
- Tomcat
La partie Front sera lancé à l'aide d'un `npm run dev` ou d'un `yarn dev`.
## GeoServer & Postgis
Afin de mettre en place la partie Postgis/Geoserver (postgis étant vide) nous avons suivis les étape suivantes:
1. Authentification dans GeoServer

2. Accès a la partie Espace de travail et création de geogo

3. Une fois l'espace de travail créer, il nous faut créer l'entrepot en suivant les informations du docker compose (identifiant : postgres:postgres@postgis:5432/postgres)

4. Ensuite il faut créer la layer correspondant à une nouvelle table que l'on créer avec l'utilitaire de geoserver

5. Une fois la table créée, il est nécessaire de renseigner l'emprise
6. A l'aide d'un outils d'accès à la base de donnée on peut insérer des points proches de notre utilisateur.
```sql
INSERT INTO PUBLIC.ressource(id,coo,RADIUS) VALUES(1,st_geomfromtext('POINT(-0.4592 46.3232)',4326),10);
INSERT INTO PUBLIC.ressource(id,coo,RADIUS) VALUES(1,st_geomfromtext('POINT(-0.4588 46.3222)',4326),10);
INSERT INTO PUBLIC.ressource(id,coo,RADIUS) VALUES(1,st_geomfromtext('POINT(-0.4575 46.3217)',4326),10);
```
## Tomcat & Servlet
Pour des soucis de simplicité, le projet a été généré via `Intelij` et `Maven`.
Ce projet consiste d'un `web.xml`, d'une classe `Geoadd.java` et d'un pom.xml :
web.xml :
```webxml
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Bonjour Application</display-name>
<description>
Geogo servelet
</description>
<servlet>
<servlet-name>Geoadd</servlet-name>
<servlet-class>org.example.Geoadd</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Geoadd</servlet-name>
<url-pattern>/geogo</url-pattern>
</servlet-mapping>
</web-app>
```
Geoaad.java:
```java
package org.example;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Geoadd extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("FULL JSON");
}
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String longCoo = req.getParameter("long");
String latCoo = req.getParameter("lat");
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
if (latCoo==""||latCoo==null || longCoo == ""||longCoo == null){
out.println("Empty param");
return;
}
try {
Class.forName("org.postgresql.Driver");
Connection connection = DriverManager.getConnection("jdbc:postgresql://postgis:5432/postgres?user=postgres&password=postgres");
PreparedStatement preparedStatement;
preparedStatement = connection.prepareStatement("DELETE FROM PUBLIC.ressource WHERE st_intersects(coo,st_geomfromtext('POINT(" +
longCoo + // Longitude -0.457482
" " +
latCoo + // Latitude 46.321705
")', 4326))");
int nbr = preparedStatement.executeUpdate();
out.println(nbr);
connection.close();
} catch (SQLException | ClassNotFoundException e) {
out.println("<big> Error accessing database </big>");
out.println("<code>" +e+ "</code>");
}
}
}
```
Pom.xml :
```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>server</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>7.0.42</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
```
Afin de déployer, si le serveur tomcat est démarré, il est nécessaire de faire un `mvn clean package` puis de copier coller le .war dans le dossier ./data/webapps qui correspond au dossier webapps du serveur tomcat.
Ce servlet expose deux endpoints :
- POST : /{nom du fichier war sans le .war}/geoaad => permet de supprimer un geo point a l'aide de deux paramétre querry (lat & long)
- GET : /{nom du fichier war sans le .war}/geoaad => Permet uniquement de s'assurer que le déploiement est finis
## Front
La partie front est initié a l'aide de la dernière version de Vite avec son template vanillaJs.

Pour démarrer le serveur de dev lié au projet, il faut lancer la commande `yarn dev` ou `npm run dev`. Le serveur sera donc accessible sur le port :5173.
La partie front est découpée en 4 fichiers important :
Package.json :
```json
{
"name": "front",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^3.2.3"
},
"dependencies": {
"ol": "7.1.0"
}
}
```
index.html :
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Geolocation</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<link rel="stylesheet" href="/style.css"/>
</head>
<body>
<div id="map" class="map"></div>
<div id="info" style="display: none;"></div>
<label for="track">
track position
<input id="track" type="checkbox"/>
</label>
<p>
position accuracy : <code id="accuracy"></code>
altitude : <code id="altitude"></code>
altitude accuracy : <code id="altitudeAccuracy"></code>
heading : <code id="heading"></code>
speed : <code id="speed"></code>
</p>
<!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer -->
<script src="https://unpkg.com/elm-pep@1.0.6/dist/elm-pep.js"></script>
<script type="module" src="/main.js"></script>
</body>
</html>
```
Main.js
```javascript
import Feature from "ol/Feature";
import Geolocation from "ol/Geolocation";
import Map from "ol/Map";
import Point from "ol/geom/Point";
import View from "ol/View";
import { Circle as CircleStyle, Fill, Stroke, Style } from "ol/style";
import { OSM, Vector as VectorSource } from "ol/source";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { fromLonLat } from "ol/proj";
import { Circle } from "ol/geom";
import { intersects } from "ol/extent";
const view = new View({
center: fromLonLat([-0.45877, 46.32211]),
zoom: 19,
});
const map = new Map({
layers: [
new TileLayer({
source: new OSM(),
}),
],
target: "map",
view: view,
});
const geolocation = new Geolocation({
// enableHighAccuracy must be set to true to have the heading value.
trackingOptions: {
enableHighAccuracy: true,
},
projection: view.getProjection(),
});
function el(id) {
return document.getElementById(id);
}
el("track").addEventListener("change", function () {
geolocation.setTracking(this.checked);
});
// update the HTML page when the position changes.
geolocation.on("change", function () {
el("accuracy").innerText = geolocation.getAccuracy() + " [m]";
el("altitude").innerText = geolocation.getAltitude() + " [m]";
el("altitudeAccuracy").innerText = geolocation.getAltitudeAccuracy() + " [m]";
el("heading").innerText = geolocation.getHeading() + " [rad]";
el("speed").innerText = geolocation.getSpeed() + " [m/s]";
});
// handle geolocation error.
geolocation.on("error", function (error) {
const info = document.getElementById("info");
info.innerHTML = error.message;
info.style.display = "";
});
const positionFeature = new Feature();
positionFeature.setStyle(
new Style({
image: new CircleStyle({
radius: 6,
fill: new Fill({
color: "#ee0000",
}),
stroke: new Stroke({
color: "#fff",
width: 2,
}),
}),
})
);
const coordinates = fromLonLat([-0.458654, 46.322057]);
positionFeature.setGeometry(new Point(coordinates));
let vectorLayer = new VectorLayer({
map: map,
source: new VectorSource({
features: [positionFeature],
}),
});
let points = [];
let radius = 5;
let baseUrl = "localhost";
let urlposition = `http://${baseUrl}:8081/geoserver/geogo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=geogo%3Aressource&maxFeatures=50&outputFormat=application%2Fjson`;
let circles = [];
fetch(urlposition)
.then((response) => response.json())
.then((data) => {
for (let feature in data.features) {
let point = new Feature();
let coord = fromLonLat(data.features[feature].geometry.coordinates);
point.setGeometry(new Point(coord));
point.setStyle(
new Style({
image: new CircleStyle({
radius: 6,
fill: new Fill({
color: "#3399CC",
}),
stroke: new Stroke({
color: "#fff",
width: 2,
}),
}),
})
);
point.dispose();
let circle = new Circle(coord, 10);
let circleFeature = new Feature(circle);
let circleToStore = {
initialCoord: data.features[feature].geometry.coordinates,
coord: coord,
radius: radius,
circle: circle,
circleFeature: circleFeature,
point: point,
};
points.push(point);
points.push(circleFeature);
circles.push(circleToStore);
}
window.setInterval(function () {
for (let circle in circles) {
circles[circle]["radius"] += 5;
circles[circle]["circleFeature"].setGeometry(
new Circle(circles[circle]["coord"], circles[circle]["radius"])
);
let intersect = circles[circle]["circleFeature"]
.getGeometry()
.intersectsCoordinate(positionFeature.getGeometry().flatCoordinates);
if (intersect) {
fetch(
`http://${baseUrl}:8080/server/geogo?long=${circles[circle]["initialCoord"][0]}&lat=${circles[circle]["initialCoord"][1]}`,
{
method: "POST",
mode: "no-cors",
}
);
circles[circle]["circleFeature"];
vectorLayer
.getSource()
.removeFeature(circles[circle]["circleFeature"]);
vectorLayer.getSource().removeFeature(circles[circle]["point"]);
circles = circles.filter(function (ele) {
return ele != circles[circle];
});
}
}
}, 1000);
})
.then(() => {
let features = points.concat([positionFeature]);
vectorLayer.getSource().addFeatures(points);
});
```
Et enfin le style.css :
```css
.map {
width: 100%;
height: 400px;
}
```