Skip to content

Commit e3005cd

Browse files
THS-LMISThomas Schley
andauthored
Hinzufügen von Funktionalitäten der Kartendarstellung auf der Startseite (#14)
Co-authored-by: Thomas Schley <[email protected]>
1 parent f5f9f06 commit e3005cd

File tree

13 files changed

+353
-44
lines changed

13 files changed

+353
-44
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VITE_FIWARE_SERVER_BASE_URL=

.github/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ADD . /app/
88
WORKDIR /app
99

1010
# install app
11+
ARG VITE_FIWARE_SERVER_BASE_URL=localhost
12+
ENV VITE_FIWARE_SERVER_BASE_URL=$VITE_FIWARE_SERVER_BASE_URL
1113
RUN npm install && npm run build
1214

1315
# expose web port

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
# misc
1717
.DS_Store
18+
.env
1819
.env.local
1920
.env.development.local
2021
.env.test.local

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Getting Started with 5GLa Visualization
22

3+
## Create .env
4+
5+
Rename/copy `.env.example` to `.env` and change the `.env` with your configuration.
6+
7+
## Scripts
8+
39
In the project directory, you can run:
410

511
### `npm run dev`
@@ -26,8 +32,8 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
2632
## Run with Docker
2733

2834
```shell
29-
docker build -f ./.github/Dockerfile -t 5gla-react-visualization .
30-
docker run --name 5gla-react-visualization -p 3000:3000 5gla-react-visualization
35+
docker build --build-arg VITE_FIWARE_SERVER_BASE_URL=<url of Fiware server> -f ./.github/Dockerfile -t 5gla-react-visualization .
36+
docker run --name 5gla-react-visualization -p 3000:3000 5gla-react-visualization
3137
```
3238

3339
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

app/components/Breadcrumbs.module.css

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
color: white;
77
font-size: large;
88
text-decoration: none;
9-
& + &:before {
10-
content: '>';
11-
font-weight: normal;
12-
margin: 0 15px;
13-
}
14-
&.active {
15-
font-weight: bold;
16-
}
9+
}
10+
a + a:before {
11+
content: '>';
12+
font-weight: normal;
13+
margin: 0 15px;
14+
}
15+
a.active {
16+
font-weight: bold;
1717
}
1818
}

app/components/Breadcrumbs.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@ function Breadcrumbs() {
1212
<nav aria-label="breadcrumb" className={styles.breadcrumbs}>
1313
<NavLink
1414
to="/"
15-
className={({ isActive }) => (isActive ? styles.active : undefined)}>
15+
className={({ isActive }) => (isActive ? styles.active : undefined)}
16+
key="home"
17+
>
1618
Startseite
1719
</NavLink>
1820
{pathnames.map((pathname, index) => {
1921
const to = `/${pathnames.slice(0, index + 1).join('/')}`;
2022
const label: string = routeLabels[pathname] || pathname;
2123
return <NavLink
2224
to={to}
23-
className={({ isActive }) => (isActive ? styles.active : undefined)}>
25+
className={({ isActive }) => (isActive ? styles.active : undefined)}
26+
key={pathname}
27+
>
2428
{decodeURIComponent(label)}
2529
</NavLink>;
2630
})}

app/components/OpenLayers.tsx

Lines changed: 177 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,199 @@
1-
import React, { useEffect } from 'react';
1+
import React, {useEffect} from 'react';
22

3+
import {Feature, Map, View} from 'ol';
4+
import {Coordinate} from 'ol/coordinate';
5+
import {Extent} from "ol/extent";
6+
import {Point, Polygon} from 'ol/geom';
37
import TileLayer from 'ol/layer/Tile';
4-
import { OSM } from 'ol/source';
5-
import { Map, View } from 'ol';
8+
import VectorLayer from "ol/layer/Vector";
9+
import {fromLonLat} from 'ol/proj';
10+
import {OSM} from 'ol/source';
11+
import VectorSource from 'ol/source/Vector';
12+
import {Fill, Stroke, Style} from 'ol/style';
613

714
import 'ol/ol.css';
815
import styles from './OpenLayers.module.css';
16+
import {getAgriCropPolygon, getAgvolutionSensorsLocations, getSentekSensorsLocations} from '../services/fiwareService';
917

1018
interface Props {
1119
id: string
1220
}
1321

14-
function OpenLayers({ id }: Props) {
15-
useEffect(() => {
16-
const osmLayer = new TileLayer({
17-
preload: Infinity,
18-
source: new OSM(),
22+
interface SensorResponse {
23+
id: string,
24+
type: string,
25+
location: {
26+
type: string,
27+
coordinates: Coordinate
28+
} | null
29+
}
30+
31+
interface Sensor {
32+
id: string,
33+
type: string,
34+
coordinates: Coordinate
35+
}
36+
37+
interface AgriCropResponse {
38+
id: string,
39+
type: string,
40+
location: {
41+
type: string,
42+
coordinates: Coordinate[]
43+
}
44+
}
45+
46+
interface AgriCrop {
47+
id: string,
48+
coordinates: Coordinate[]
49+
}
50+
51+
function removeSensorByIdAndType(sensors: Sensor[], id: string, type: string) {
52+
const index: number = sensors
53+
.findIndex((sensor: Sensor): boolean => sensor.id === id && sensor.type === type);
54+
if (index >= 0) {
55+
delete sensors[index];
56+
}
57+
}
58+
59+
function removeAgriCropById(agriCrops: AgriCrop[], id: string) {
60+
const index: number = agriCrops.findIndex((agriCrop: AgriCrop) => agriCrop.id === id);
61+
if (index > 0) {
62+
delete agriCrops[index];
63+
}
64+
}
65+
66+
function updateSensors(map: Map, vectorSource: VectorSource<Feature<Point>>, sensors: Sensor[]) {
67+
const features: Feature<Point>[] = [];
68+
sensors.map((sensor: Sensor) => {
69+
features.push(new Feature({ geometry: new Point(fromLonLat(sensor.coordinates)) }));
70+
});
71+
vectorSource.clear();
72+
vectorSource.addFeatures(features);
73+
}
74+
75+
function updateAgriCrops(map: Map, vectorSource: VectorSource<Feature<Polygon>>, agriCrops: AgriCrop[]) {
76+
const features: Feature<Polygon>[] = [];
77+
agriCrops.map((agriCrop: AgriCrop) => {
78+
const lonLatCoordinates: Coordinate[] = [];
79+
agriCrop.coordinates.map((coordinate) => {
80+
lonLatCoordinates.push(fromLonLat(coordinate));
1981
})
82+
const polygonFeature = new Feature({ geometry: new Polygon([lonLatCoordinates]) });
83+
polygonFeature.setStyle(style);
84+
features.push(polygonFeature);
85+
});
86+
vectorSource.clear();
87+
vectorSource.addFeatures(features);
88+
fitMap(map, vectorSource.getExtent());
89+
}
90+
91+
function fitMap(map: Map, extent: Extent | undefined) {
92+
if (extent && extent.length === 4 && extent.every((element: number): boolean => isFinite(element))) {
93+
map.getView().fit(extent, { padding: [50, 50, 50, 50] });
94+
}
95+
}
96+
97+
function handleSensorsResponse(_sensors: SensorResponse[], map: Map, vectorSource: VectorSource<Feature<Point>>, sensors: Sensor[]) {
98+
if (Array.isArray(_sensors)) {
99+
_sensors.map((_sensor: SensorResponse) => {
100+
removeSensorByIdAndType(sensors, _sensor.id, _sensor.type);
101+
if (_sensor.location !== null) {
102+
sensors.push({
103+
id: _sensor.id,
104+
type: _sensor.type,
105+
coordinates: _sensor.location.coordinates
106+
});
107+
}
108+
updateSensors(map, vectorSource, sensors);
109+
});
110+
}
111+
}
112+
113+
function handleAgriCropResponse(_agriCrops: AgriCropResponse[], map: Map, vectorSource: VectorSource<Feature<Polygon>>, agriCrops: AgriCrop[]) {
114+
if (Array.isArray(_agriCrops)) {
115+
_agriCrops.map((_agriCrop: AgriCropResponse) => {
116+
removeAgriCropById(agriCrops, _agriCrop.id)
117+
agriCrops.push({
118+
id: _agriCrop.id,
119+
coordinates: _agriCrop.location.coordinates
120+
});
121+
});
122+
updateAgriCrops(map, vectorSource, agriCrops);
123+
}
124+
}
125+
126+
const style = new Style({
127+
fill: new Fill({
128+
color: 'rgba(0, 128, 255, 0.4)',
129+
}),
130+
stroke: new Stroke({
131+
color: 'blue',
132+
width: 2,
133+
}),
134+
});
135+
136+
function OpenLayers({ id }: Props) {
20137

138+
const osmLayer = new TileLayer({
139+
preload: Infinity,
140+
source: new OSM(),
141+
});
142+
143+
const pointFeatures: Feature<Point>[] = [];
144+
const polygonFeatures: Feature<Polygon>[] = [];
145+
const sensors: Sensor[] = [];
146+
const agriCrops: AgriCrop[] = [];
147+
148+
const pointVectorSource = new VectorSource({ features: pointFeatures });
149+
const polygonVectorSource = new VectorSource({ features: polygonFeatures });
150+
151+
const pointVectorLayer = new VectorLayer({ source: pointVectorSource });
152+
const polygonVectorLayer = new VectorLayer({ source: polygonVectorSource });
153+
154+
useEffect(() => {
21155
const map = new Map({
22156
target: id,
23-
layers: [ osmLayer ],
157+
layers: [ osmLayer, polygonVectorLayer, pointVectorLayer ],
24158
view: new View({
25159
center: [0, 0],
26160
zoom: 0,
27161
}),
28162
});
163+
164+
getAgvolutionSensorsLocations()
165+
.then((response) => handleSensorsResponse(
166+
response.data,
167+
map,
168+
pointVectorSource,
169+
sensors
170+
))
171+
.catch((error) => {
172+
console.debug(error);
173+
});
174+
175+
getSentekSensorsLocations()
176+
.then((response) => handleSensorsResponse(
177+
response.data,
178+
map,
179+
pointVectorSource,
180+
sensors
181+
))
182+
.catch((error) => {
183+
console.debug(error);
184+
});
185+
186+
getAgriCropPolygon()
187+
.then((response) => handleAgriCropResponse(
188+
response.data,
189+
map,
190+
polygonVectorSource,
191+
agriCrops
192+
))
193+
.catch((error) => {
194+
console.debug(error);
195+
});
196+
29197
return () => map.setTarget(undefined)
30198
});
31199

app/services/fiwareService.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import axios, {AxiosInstance} from 'axios';
2+
3+
const baseUrlFiwareServer: string = process.env.NODE_ENV === 'development' ? 'http://localhost:5173' : import.meta.env.VITE_FIWARE_SERVER_BASE_URL;
4+
const entitiesApiUrlFiwareServer: string = baseUrlFiwareServer + '/v2/entities/';
5+
6+
function getRequestInstanceFromFiwareServer(): AxiosInstance {
7+
return axios.create({
8+
baseURL: entitiesApiUrlFiwareServer,
9+
headers: { 'fiware-service': 'dev' }
10+
});
11+
}
12+
13+
function getRequestFromFiwareServer(params = {}, headers = {}) {
14+
return getRequestInstanceFromFiwareServer().get(entitiesApiUrlFiwareServer, { headers: headers, params: params });
15+
}
16+
17+
export function getAgvolutionSensorsLocations() {
18+
return getRequestFromFiwareServer({
19+
type: 'AgvolutionSensor',
20+
attrs: 'location',
21+
options: 'keyValues'
22+
});
23+
}
24+
25+
export function getSentekSensorsLocations() {
26+
return getRequestFromFiwareServer({
27+
type: 'SentekSensor',
28+
attrs: 'location',
29+
options: 'keyValues'
30+
});
31+
}
32+
33+
export function getAgriCropPolygon() {
34+
return getRequestFromFiwareServer({
35+
type: 'AgriCrop',
36+
attrs: 'location',
37+
options: 'keyValues'
38+
});
39+
}

0 commit comments

Comments
 (0)