Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend polishing - part 1 #224

Merged
merged 10 commits into from
Jun 2, 2024
15 changes: 2 additions & 13 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,11 @@
max-height: 100vh;
}

.header {
display: flex;
vertical-align: middle;
height: 10vh;
padding: 1rem 2rem;
margin: 0;
font-size: 1.2rem;
font-weight: 500;
align-items: center;
}

.content-container {
display: flex;
flex-direction: row;
height: 90vh;
padding: 0rem 2rem 1rem 2rem;
height: 100vh;
padding: 1rem 2rem 1rem 2rem;
gap: 1rem;
}

Expand Down
1 change: 0 additions & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ function App() {
<MainMenu />
) : (
<Fragment>
<div className="header">Building Information Enhancer</div>
<div className="content-container">
<MultiMap />
<DataView />
Expand Down
29 changes: 25 additions & 4 deletions frontend/src/components/DatasetsList/DatasetsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import {
ListItemButton,
ListItemText,
} from "@mui/material";
import { ChargingStation, Icon, Blueprint } from "@phosphor-icons/react";
import {
ChargingStation,
Icon,
Blueprint,
MapTrifold,
} from "@phosphor-icons/react";
import { useContext } from "react";
import { TabProps, TabsContext } from "../../contexts/TabsContext";

Expand All @@ -17,12 +22,19 @@ import L, { Icon as LIcon, DivIcon } from "leaflet";
import { createRoot } from "react-dom/client";
import { flushSync } from "react-dom";

// Enum for types of markers
enum MarkersTypes {
Markers = "markers", // Map will display single coordinates with markers on top.
Areas = "areas", // Map will display polygon areas.
None = "none", // Map will not display anything.
}

// Dataset Type
export type Dataset = {
id: string;
displayName: string;
description: string;
type: string;
type: MarkersTypes;
datasetIcon: Icon;
markerIcon: LIcon | DivIcon | undefined;
data: FeatureCollection;
Expand Down Expand Up @@ -52,11 +64,20 @@ const divIconChargingStation: DivIcon = L.divIcon({
});

const datasetsData: Dataset[] = [
{
id: "empty_map",
displayName: "Empty Map",
description: "An empty, default map of Germany, with no data loaded.",
type: MarkersTypes.None,
datasetIcon: MapTrifold,
markerIcon: undefined,
data: emptyFeatureCollection,
},
{
id: "charging_stations",
displayName: "Charging stations",
description: "Locations of all charging stations in Germany.",
type: "markers",
type: MarkersTypes.Markers,
datasetIcon: ChargingStation,
markerIcon: divIconChargingStation,
data: emptyFeatureCollection,
Expand All @@ -65,7 +86,7 @@ const datasetsData: Dataset[] = [
id: "house_footprints",
displayName: "House Footprints",
description: "Footprints for the hauses.",
type: "areas",
type: MarkersTypes.Areas,
datasetIcon: Blueprint,
markerIcon: undefined,
data: emptyFeatureCollection,
Expand Down
24 changes: 15 additions & 9 deletions frontend/src/components/MapView/DataFetch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ const geojsonGemeindenPolygons: FeatureCollection =

// Define the base of the API URL
const getBaseApiUrl = () => {
console.log("Checking base url: " + import.meta.env.VITE_STAGE);
switch (import.meta.env.VITE_STAGE) {
case "production":
return `http://${import.meta.env.VITE_API_HOST_PRODUCTION}:${
Expand All @@ -23,10 +22,10 @@ const getBaseApiUrl = () => {
import.meta.env.VITE_API_PORT_TEST
}`;
default:
return `http://${import.meta.env.VITE_API_HOST_DEV}:${
import.meta.env.VITE_API_PORT_DEV
}`;
}
return `http://${import.meta.env.VITE_API_HOST_DEV}:${
import.meta.env.VITE_API_PORT_DEV
}`;
};

const useGeoData = (
Expand All @@ -41,6 +40,8 @@ const useGeoData = (
// Returns the API URL of the endpoint for a specific dataset
const getApiUrlForDataset = (): string => {
switch (id) {
case "empty_map":
return "";
case "charging_stations":
return getBaseApiUrl() + "/api/v1.0/Dataset/1/data";
case "house_footprints":
Expand All @@ -59,6 +60,10 @@ const useGeoData = (
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const fetchData = async (_bounds: LatLngBounds): Promise<void> => {
// Skip if just an empty map was loaded
if (id === "empty_map") {
return;
}
try {
// const bottomLat = bounds.getSouth();
// const bottomLong = bounds.getWest();
Expand All @@ -73,6 +78,7 @@ const useGeoData = (
TopLong: 49, //topLong,
ZoomLevel: zoom,
};
console.log(getApiUrlForDataset());
const response = await axios.get<FeatureCollection<Geometry>>(
getApiUrlForDataset(),
{
Expand All @@ -83,11 +89,11 @@ const useGeoData = (
onUpdate(response.data);
} catch (error) {
// Display alert
setCurrentAlertCache({
...currentAlertCache,
isAlertOpened: true,
text: "Fetching data failed, using local GeoJSON data.",
});
// setCurrentAlertCache({
// ...currentAlertCache,
// isAlertOpened: true,
// text: "Fetching data failed, using local GeoJSON data.",
// });
console.error("Fetching data failed, using local GeoJSON data.", error);
// Console log the error
if (axios.isAxiosError(error)) {
Expand Down
85 changes: 85 additions & 0 deletions frontend/src/components/MapView/MapEventsHandler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useContext } from "react";
import { Marker } from "react-leaflet/Marker";
import { Popup } from "react-leaflet/Popup";
import { useMap, useMapEvents } from "react-leaflet/hooks";
import { MapContext } from "../../contexts/MapContext";
import L, { DivIcon } from "leaflet";
import { MapPin } from "@phosphor-icons/react";
import { createRoot } from "react-dom/client";
import { flushSync } from "react-dom";

// Utility function to render a React component to HTML string
const renderToHtml = (Component: React.FC) => {
const div = document.createElement("div");
const root = createRoot(div);
flushSync(() => {
root.render(<Component />);
});
return div.innerHTML;
};

const divIconMarker: DivIcon = L.divIcon({
html: renderToHtml(() => <MapPin size={36} color="#ff0000" weight="fill" />),
className: "", // Optional: add a custom class name
iconSize: [36, 36],
iconAnchor: [18, 36], // Adjust the anchor point as needed
});

const MapEventsHandler = () => {
const { currentMapCache, setCurrentMapCache } = useContext(MapContext);

const setPosition = (latlng: L.LatLng) => {
setCurrentMapCache({ ...currentMapCache, selectedCoordinates: latlng });
};

const map = useMap();
// Add events
useMapEvents({
click: (event) => {
console.log(event);
setCurrentMapCache({
...currentMapCache,
selectedCoordinates: event.latlng,
});
},
moveend: (event) => {
setCurrentMapCache({
...currentMapCache,
mapCenter: event.target.getCenter(),
mapBounds: event.target.getBounds(),
zoom: event.target.getZoom(),
});
},
});

return (
<Marker position={currentMapCache.selectedCoordinates} icon={divIconMarker}>
<Popup>
<span
// Get the current location of the user
onClick={() => {
map
.locate({ setView: true })
.on("locationfound", function (event) {
setPosition(event.latlng);
map.flyTo(event.latlng, map.getZoom(), {
animate: true,
duration: 50,
});
})
// If access to the location was denied
.on("locationerror", function (event) {
console.log(event);
alert("Location access denied.");
});
}}
>
{currentMapCache.selectedCoordinates.lat.toFixed(4)},{" "}
{currentMapCache.selectedCoordinates.lng.toFixed(4)}
</span>
</Popup>
</Marker>
);
};

export default MapEventsHandler;
51 changes: 41 additions & 10 deletions frontend/src/components/MapView/MapOptions.css
Original file line number Diff line number Diff line change
@@ -1,22 +1,53 @@
.search-map-icon {
width: 2rem;
height: 2rem;
.search-map-icon-container {
width: 2.1rem;
height: 2.1rem;
position: absolute;
bottom: 1.5rem;
right: 1rem;
top: 7.5rem;
right: 0.6rem;
cursor: pointer;
z-index: 2;
display: flex;
flex-direction: column;
background-color: white;
justify-content: center;
align-items: center;
border: 2px solid rgba(0, 0, 0, 0.27);
box-shadow: none;
}

.search-map-icon-container:hover {
background-color: #f4f4f4;
color: #535bf2;
}

.search-map-icon {
width: 1.5rem;
height: 1.5rem;
}
.switch-map-icon {
width: 2rem;
height: 2rem;

.layers-map-icon-container {
width: 2.1rem;
height: 2.1rem;
position: absolute;
bottom: 4rem;
right: 1rem;
top: 5rem;
right: 0.6rem;
cursor: pointer;
z-index: 2;
display: flex;
flex-direction: column;
background-color: white;
justify-content: center;
align-items: center;
border: 2px solid rgba(0, 0, 0, 0.27);
box-shadow: none;
}

.layers-map-icon-container:hover {
background-color: #f4f4f4;
color: #535bf2;
}

.layers-map-icon {
width: 1.5rem;
height: 1.5rem;
}
25 changes: 12 additions & 13 deletions frontend/src/components/MapView/MapOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from "react";
import { ArrowsClockwise, MagnifyingGlass } from "@phosphor-icons/react";
import { Stack, MagnifyingGlass } from "@phosphor-icons/react";
import "./MapOptions.css";
import { Tooltip } from "@mui/material";
import SearchPopUp from "../PopUp/SearchPopUp";
Expand All @@ -17,19 +17,18 @@ const MapOptions: React.FC<MapOptionsProps> = ({ toggleShowSatellite }) => {

return (
<div className="map-options-container">
<Tooltip arrow title="Search for an address">
<MagnifyingGlass
weight="duotone"
className="search-map-icon"
onClick={toggleIfOpenedDialog}
/>
<Tooltip arrow title="Search for an address" placement="right">
<div className="search-map-icon-container leaflet-bar leaflet-control leaflet-control-custom">
<MagnifyingGlass
className="search-map-icon"
onClick={toggleIfOpenedDialog}
/>
</div>
</Tooltip>
<Tooltip arrow title="Switch satellite / openstreetmap">
<ArrowsClockwise
weight="duotone"
className="switch-map-icon"
onClick={toggleShowSatellite}
/>
<Tooltip arrow title="Switch layers" placement="right">
<div className="layers-map-icon-container leaflet-touch leaflet-bar leaflet-control leaflet-control-custom">
<Stack className="layers-map-icon" onClick={toggleShowSatellite} />
</div>
</Tooltip>
<SearchPopUp
onToggleIfOpenedDialog={toggleIfOpenedDialog}
Expand Down
Loading
Loading