Skip to content

Commit

Permalink
Merge pull request #38 from DHBW-FN-TIT20/KWYG-Sprint-1
Browse files Browse the repository at this point in the history
Kwyg sprint 1
  • Loading branch information
Floqueboque authored Dec 26, 2022
2 parents 5dc135c + 49cf6e9 commit 96f14fc
Show file tree
Hide file tree
Showing 13 changed files with 865 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ Thumbs.db

# Production build
www/

# Pagekite for localhost tunneling (https://pagekite.net/)
pagekite.py
30 changes: 30 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
"framework7": "^7.0.9",
"framework7-icons": "^5.0.5",
"framework7-react": "^7.0.9",
"leaflet": "^1.9.3",
"material-icons": "^1.13.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-leaflet": "^4.2.0",
"skeleton-elements": "^4.0.1",
"swiper": "^8.4.5"
},
Expand Down
Binary file added public/img/LocationMarker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/img/OwnLocationMarker.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions src/components/AccuracyCircle.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import { Circle } from "react-leaflet";

class AccuracyCircle extends React.Component {
render() {
if (
this.props.visible === false ||
this.props.center === undefined ||
this.props.center === null ||
this.props.center.lat === undefined ||
this.props.center.lng === undefined ||
this.props.radius === undefined ||
this.props.radius === null
) {
return null;
}

return (
<Circle
center={this.props.center}
radius={this.props.radius}
pathOptions={{ fill: true, fillColor: "#0000ff", fillOpacity: 0.1, color: "#0000ff", opacity: 0.2 }}
/>
);
}
}

// define the types of the properties that are passed to the component
AccuracyCircle.prototype.props = /** @type { {
visible: boolean,
center: { lat: number, lng: number } | undefined,
radius: number | undefined,
} } */ ({});

export default AccuracyCircle;
28 changes: 28 additions & 0 deletions src/components/LocationMarker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { Marker } from "react-leaflet";
import L from "leaflet";

class LocationMarker extends React.Component {
markerIcon = L.icon({
iconUrl: this.props.iconUrl,
iconSize: [this.props.size.width, this.props.size.height],
// Center of the icon:
iconAnchor: [this.props.anchor?.x || this.props.size.width / 2, this.props.anchor?.y || this.props.size.height / 2],
});

render() {
if (
this.props.position === undefined ||
this.props.position === null ||
this.props.position.lat === undefined ||
this.props.position.lng === undefined ||
!this.props.visible
) {
return null;
}

return <Marker position={[this.props.position.lat, this.props.position.lng]} icon={this.markerIcon} />;
}
}

export default LocationMarker;
86 changes: 86 additions & 0 deletions src/components/OutlinePolygon.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from "react";
import { Polygon } from "react-leaflet";

class OutlinePolygon extends React.Component {
constructor(props) {
super(props);
this.state = {
polyLatLngs: [],
};
}

async componentDidUpdate(prevProps) {
if (this.props.placeName === prevProps.placeName) return;
this.setState({ polyLatLngs: [] });

// fetch the polygon outline from the OpenStreetMap API (Nominatim)
// this is not done in the main component because loading the polygon outline
// takes more time than the normal API call
const response = await fetch(
// eslint-disable-next-line max-len
`https://nominatim.openstreetmap.org/search?q=${this.props.placeName}&format=json&limit=1&polygon_geojson=1&polygon_threshold=0.0005`,
);
// check if the response is suited for a polygon outline
const data = await response.json();
if (
data[0]?.geojson?.coordinates === undefined ||
data[0]?.geojson?.type === undefined ||
data[0]?.boundingbox === undefined ||
(data[0]?.geojson?.type !== "Polygon" && data[0]?.geojson?.type !== "MultiPolygon") ||
(data[0]?.class !== "boundary" && data[0]?.class !== "place" && data[0]?.class !== "landuse")
) {
this.setState({ polyLatLngs: [] });
return;
}

const latLngs = this.convertGeoJsonCoordsToLeafletLatLng(data[0].geojson.coordinates);
this.setState({ polyLatLngs: latLngs });
}

/**
* Converts the coordinates of a GeoJSON polygon to the format used by Leaflet
* @param {number[]} coords The coordinates of the GeoJSON polygon
* @returns {number[]} The coordinates for the Leaflet polygon
*/
convertGeoJsonCoordsToLeafletLatLng = coords => {
if (coords === undefined) return [];

const transformCoords = coords => {
if (
Array.isArray(coords) &&
coords.length === 2 &&
typeof coords[0] === "number" &&
typeof coords[1] === "number"
) {
return { lat: coords[1], lng: coords[0] };
}
return coords.map(transformCoords);
};

const latLngs = transformCoords(coords);

return latLngs;
};

render() {
if (this.state.polyLatLngs.length === 0) return null;

return (
<Polygon
positions={this.state.polyLatLngs}
color="blue"
weight={1}
opacity={0.2}
fillOpacity={0.05}
smoothFactor={1}
/>
);
}
}

// define the types of the properties that are passed to the component
OutlinePolygon.prototype.props = /** @type { {
placeName: string,
} } */ ({});

export default OutlinePolygon;
151 changes: 151 additions & 0 deletions src/components/SnappingSheet.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React from "react";
import { Sheet } from "framework7-react";

class SnappingSheet extends React.Component {
dragStartPositionY = 0;
sheetScrollAreaRef = React.createRef();
isMoving = false;

constructor(props) {
super(props);
this.state = {
sheetOpened: true,
sheetHeight: this.props.snapHeightStates[this.props.currentState || 0],
sheetHeightTransitionStyle: "0.3s ease-in-out",
};
}

/**
* Adds touch and mouse listeners to the sheet
*/
componentDidMount() {
// add touch listeners to the sheet
this.sheetScrollAreaRef.current.addEventListener("touchstart", this.dragStart, { passive: false });
this.sheetScrollAreaRef.current.addEventListener("touchmove", this.dragMove, { passive: false });
this.sheetScrollAreaRef.current.addEventListener("touchend", this.dragEnd, { passive: false });

// add mouse listeners to the sheet
this.sheetScrollAreaRef.current.addEventListener("mousedown", this.dragStart, { passive: false });
window.addEventListener("mousemove", this.dragMove, { passive: false });
window.addEventListener("mouseup", this.dragEnd, { passive: false });

// let the parent know that the sheet is snapped to the current state (to clear the state)
if (this.props.currentState !== undefined) this.props.snappedToHeight(this.props.currentState);
}

/**
* Removes touch and mouse listeners from the sheet
*/
componentWillUnmount() {
// remove touch listeners from the sheet
this.sheetScrollAreaRef.current.removeEventListener("touchstart", this.dragStart);
this.sheetScrollAreaRef.current.removeEventListener("touchmove", this.dragMove);
this.sheetScrollAreaRef.current.removeEventListener("touchend", this.dragEnd);

// remove mouse listeners from the sheet
this.sheetScrollAreaRef.current.removeEventListener("mousedown", this.dragStart);
window.removeEventListener("mousemove", this.dragMove);
window.removeEventListener("mouseup", this.dragEnd);
}

/**
* Snaps the sheet to a given state if the state is defined
*/
componentDidUpdate() {
if (this.props.currentState !== undefined) {
this.setState({
sheetHeightTransitionStyle: "0.3s ease-in-out",
sheetHeight: this.props.snapHeightStates[this.props.currentState],
});
this.props.snappedToHeight(this.props.snapHeightStates[this.props.currentState]);
}
}

/**
* Starts the dragging process
* @param {Event} event
*/
dragStart = event => {
this.dragStartPositionY = event.touches ? event.touches[0].clientY : event.clientY;
this.isMoving = true;
this.setState({ sheetHeightTransitionStyle: "0s" });
};

/**
* Moves the sheet (param is a touch event or a mouse event)
* @param { Event } event
*/
dragMove = event => {
event.preventDefault();

if (!this.isMoving) return;

// check if it is a touch event or a mouse event and get the y position
const dragStartPositionY = event.touches ? event.touches[0].clientY : event.clientY;

// calculate the new sheet height
const difference = this.dragStartPositionY - dragStartPositionY;
const newSheetHeight = this.state.sheetHeight + difference;

// check if the new sheet height is in the allowed range
if (newSheetHeight < this.props.snapHeightStates[0]) {
this.setState({ sheetHeight: this.props.snapHeightStates[0] });
return;
}
if (newSheetHeight > this.props.snapHeightStates[this.props.snapHeightStates.length - 1]) {
this.setState({ sheetHeight: this.props.snapHeightStates[this.props.snapHeightStates.length - 1] });
return;
}

// set the new sheet height and update the touch start position
this.setState({ sheetHeight: newSheetHeight });
this.dragStartPositionY = dragStartPositionY;
};

/**
* Ends the dragging process
*/
dragEnd = () => {
this.isMoving = false;

// snap the sheet to the nearest height state
const closestSheetHeightState = this.props.snapHeightStates.reduce((prev, curr) => {
return Math.abs(curr - this.state.sheetHeight) < Math.abs(prev - this.state.sheetHeight) ? curr : prev;
});

// transition to the new sheet height
this.setState({ sheetHeightTransitionStyle: "0.3s ease-out", sheetHeight: closestSheetHeightState });
this.props.snappedToHeight(closestSheetHeightState);
};

render() {
if (this.props.children === undefined) {
return null;
}

return (
<div ref={this.sheetScrollAreaRef}>
<Sheet
style={{
height: this.state.sheetHeight,
transition: this.state.sheetHeightTransitionStyle,
overflow: "hidden",
}}
opened={true}
>
{this.props.children}
</Sheet>
</div>
);
}
}

// define the types of the properties that are passed to the component
SnappingSheet.prototype.props = /** @type { {
children: React.ReactNode
snapHeightStates: number[],
currentState: number | undefined,
snappedToHeight: (height: number) => void
} } */ ({});

export default SnappingSheet;
Loading

0 comments on commit 96f14fc

Please sign in to comment.