Skip to content

Commit

Permalink
Hinzufügen von Funktionalitäten der Kartendarstellung auf der Startse…
Browse files Browse the repository at this point in the history
…ite (#14)

Co-authored-by: Thomas Schley <thomas.schley@lmis.de>
  • Loading branch information
THS-LMIS and Thomas Schley authored Dec 16, 2024
1 parent f5f9f06 commit e3005cd
Show file tree
Hide file tree
Showing 13 changed files with 353 additions and 44 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_FIWARE_SERVER_BASE_URL=
2 changes: 2 additions & 0 deletions .github/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ ADD . /app/
WORKDIR /app

# install app
ARG VITE_FIWARE_SERVER_BASE_URL=localhost
ENV VITE_FIWARE_SERVER_BASE_URL=$VITE_FIWARE_SERVER_BASE_URL
RUN npm install && npm run build

# expose web port
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

# misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Getting Started with 5GLa Visualization

## Create .env

Rename/copy `.env.example` to `.env` and change the `.env` with your configuration.

## Scripts

In the project directory, you can run:

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

```shell
docker build -f ./.github/Dockerfile -t 5gla-react-visualization .
docker run --name 5gla-react-visualization -p 3000:3000 5gla-react-visualization
docker build --build-arg VITE_FIWARE_SERVER_BASE_URL=<url of Fiware server> -f ./.github/Dockerfile -t 5gla-react-visualization .
docker run --name 5gla-react-visualization -p 3000:3000 5gla-react-visualization
```

Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
16 changes: 8 additions & 8 deletions app/components/Breadcrumbs.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
color: white;
font-size: large;
text-decoration: none;
& + &:before {
content: '>';
font-weight: normal;
margin: 0 15px;
}
&.active {
font-weight: bold;
}
}
a + a:before {
content: '>';
font-weight: normal;
margin: 0 15px;
}
a.active {
font-weight: bold;
}
}
8 changes: 6 additions & 2 deletions app/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@ function Breadcrumbs() {
<nav aria-label="breadcrumb" className={styles.breadcrumbs}>
<NavLink
to="/"
className={({ isActive }) => (isActive ? styles.active : undefined)}>
className={({ isActive }) => (isActive ? styles.active : undefined)}
key="home"
>
Startseite
</NavLink>
{pathnames.map((pathname, index) => {
const to = `/${pathnames.slice(0, index + 1).join('/')}`;
const label: string = routeLabels[pathname] || pathname;
return <NavLink
to={to}
className={({ isActive }) => (isActive ? styles.active : undefined)}>
className={({ isActive }) => (isActive ? styles.active : undefined)}
key={pathname}
>
{decodeURIComponent(label)}
</NavLink>;
})}
Expand Down
186 changes: 177 additions & 9 deletions app/components/OpenLayers.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,199 @@
import React, { useEffect } from 'react';
import React, {useEffect} from 'react';

import {Feature, Map, View} from 'ol';
import {Coordinate} from 'ol/coordinate';
import {Extent} from "ol/extent";
import {Point, Polygon} from 'ol/geom';
import TileLayer from 'ol/layer/Tile';
import { OSM } from 'ol/source';
import { Map, View } from 'ol';
import VectorLayer from "ol/layer/Vector";
import {fromLonLat} from 'ol/proj';
import {OSM} from 'ol/source';
import VectorSource from 'ol/source/Vector';
import {Fill, Stroke, Style} from 'ol/style';

import 'ol/ol.css';
import styles from './OpenLayers.module.css';
import {getAgriCropPolygon, getAgvolutionSensorsLocations, getSentekSensorsLocations} from '../services/fiwareService';

interface Props {
id: string
}

function OpenLayers({ id }: Props) {
useEffect(() => {
const osmLayer = new TileLayer({
preload: Infinity,
source: new OSM(),
interface SensorResponse {
id: string,
type: string,
location: {
type: string,
coordinates: Coordinate
} | null
}

interface Sensor {
id: string,
type: string,
coordinates: Coordinate
}

interface AgriCropResponse {
id: string,
type: string,
location: {
type: string,
coordinates: Coordinate[]
}
}

interface AgriCrop {
id: string,
coordinates: Coordinate[]
}

function removeSensorByIdAndType(sensors: Sensor[], id: string, type: string) {
const index: number = sensors
.findIndex((sensor: Sensor): boolean => sensor.id === id && sensor.type === type);
if (index >= 0) {
delete sensors[index];
}
}

function removeAgriCropById(agriCrops: AgriCrop[], id: string) {
const index: number = agriCrops.findIndex((agriCrop: AgriCrop) => agriCrop.id === id);
if (index > 0) {
delete agriCrops[index];
}
}

function updateSensors(map: Map, vectorSource: VectorSource<Feature<Point>>, sensors: Sensor[]) {
const features: Feature<Point>[] = [];
sensors.map((sensor: Sensor) => {
features.push(new Feature({ geometry: new Point(fromLonLat(sensor.coordinates)) }));
});
vectorSource.clear();
vectorSource.addFeatures(features);
}

function updateAgriCrops(map: Map, vectorSource: VectorSource<Feature<Polygon>>, agriCrops: AgriCrop[]) {
const features: Feature<Polygon>[] = [];
agriCrops.map((agriCrop: AgriCrop) => {
const lonLatCoordinates: Coordinate[] = [];
agriCrop.coordinates.map((coordinate) => {
lonLatCoordinates.push(fromLonLat(coordinate));
})
const polygonFeature = new Feature({ geometry: new Polygon([lonLatCoordinates]) });
polygonFeature.setStyle(style);
features.push(polygonFeature);
});
vectorSource.clear();
vectorSource.addFeatures(features);
fitMap(map, vectorSource.getExtent());
}

function fitMap(map: Map, extent: Extent | undefined) {
if (extent && extent.length === 4 && extent.every((element: number): boolean => isFinite(element))) {
map.getView().fit(extent, { padding: [50, 50, 50, 50] });
}
}

function handleSensorsResponse(_sensors: SensorResponse[], map: Map, vectorSource: VectorSource<Feature<Point>>, sensors: Sensor[]) {
if (Array.isArray(_sensors)) {
_sensors.map((_sensor: SensorResponse) => {
removeSensorByIdAndType(sensors, _sensor.id, _sensor.type);
if (_sensor.location !== null) {
sensors.push({
id: _sensor.id,
type: _sensor.type,
coordinates: _sensor.location.coordinates
});
}
updateSensors(map, vectorSource, sensors);
});
}
}

function handleAgriCropResponse(_agriCrops: AgriCropResponse[], map: Map, vectorSource: VectorSource<Feature<Polygon>>, agriCrops: AgriCrop[]) {
if (Array.isArray(_agriCrops)) {
_agriCrops.map((_agriCrop: AgriCropResponse) => {
removeAgriCropById(agriCrops, _agriCrop.id)
agriCrops.push({
id: _agriCrop.id,
coordinates: _agriCrop.location.coordinates
});
});
updateAgriCrops(map, vectorSource, agriCrops);
}
}

const style = new Style({
fill: new Fill({
color: 'rgba(0, 128, 255, 0.4)',
}),
stroke: new Stroke({
color: 'blue',
width: 2,
}),
});

function OpenLayers({ id }: Props) {

const osmLayer = new TileLayer({
preload: Infinity,
source: new OSM(),
});

const pointFeatures: Feature<Point>[] = [];
const polygonFeatures: Feature<Polygon>[] = [];
const sensors: Sensor[] = [];
const agriCrops: AgriCrop[] = [];

const pointVectorSource = new VectorSource({ features: pointFeatures });
const polygonVectorSource = new VectorSource({ features: polygonFeatures });

const pointVectorLayer = new VectorLayer({ source: pointVectorSource });
const polygonVectorLayer = new VectorLayer({ source: polygonVectorSource });

useEffect(() => {
const map = new Map({
target: id,
layers: [ osmLayer ],
layers: [ osmLayer, polygonVectorLayer, pointVectorLayer ],
view: new View({
center: [0, 0],
zoom: 0,
}),
});

getAgvolutionSensorsLocations()
.then((response) => handleSensorsResponse(
response.data,
map,
pointVectorSource,
sensors
))
.catch((error) => {
console.debug(error);
});

getSentekSensorsLocations()
.then((response) => handleSensorsResponse(
response.data,
map,
pointVectorSource,
sensors
))
.catch((error) => {
console.debug(error);
});

getAgriCropPolygon()
.then((response) => handleAgriCropResponse(
response.data,
map,
polygonVectorSource,
agriCrops
))
.catch((error) => {
console.debug(error);
});

return () => map.setTarget(undefined)
});

Expand Down
39 changes: 39 additions & 0 deletions app/services/fiwareService.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import axios, {AxiosInstance} from 'axios';

const baseUrlFiwareServer: string = process.env.NODE_ENV === 'development' ? 'http://localhost:5173' : import.meta.env.VITE_FIWARE_SERVER_BASE_URL;
const entitiesApiUrlFiwareServer: string = baseUrlFiwareServer + '/v2/entities/';

function getRequestInstanceFromFiwareServer(): AxiosInstance {
return axios.create({
baseURL: entitiesApiUrlFiwareServer,
headers: { 'fiware-service': 'dev' }
});
}

function getRequestFromFiwareServer(params = {}, headers = {}) {
return getRequestInstanceFromFiwareServer().get(entitiesApiUrlFiwareServer, { headers: headers, params: params });
}

export function getAgvolutionSensorsLocations() {
return getRequestFromFiwareServer({
type: 'AgvolutionSensor',
attrs: 'location',
options: 'keyValues'
});
}

export function getSentekSensorsLocations() {
return getRequestFromFiwareServer({
type: 'SentekSensor',
attrs: 'location',
options: 'keyValues'
});
}

export function getAgriCropPolygon() {
return getRequestFromFiwareServer({
type: 'AgriCrop',
attrs: 'location',
options: 'keyValues'
});
}
Loading

0 comments on commit e3005cd

Please sign in to comment.