import { MapContainer } from 'react-leaflet/MapContainer'; import { Marker } from 'react-leaflet/Marker'; import { Popup } from 'react-leaflet/Popup'; import { Polygon } from 'react-leaflet/Polygon'; import { Tooltip } from 'react-leaflet/Tooltip'; import { useState, useEffect } from "react"; import React from 'react'; import L from 'leaflet'; import stairs from "./stairs.png"; import lift from "./lift.png"; import toilet from "./toilet.png" import { useMapEvent, useMap } from 'react-leaflet/hooks'; function Stairs({position, size, body}) { // React class (in function form) to represent stairs, takes the position where to put the stairs logo // and the body of the popup of the stairs (list of floors accessible from that staircase) const map = useMap(); const [markerIcon, setMarkerIcon] = useState(new L.Icon({ iconUrl: stairs, iconSize: size })); useMapEvent('zoomend', () => {setMarkerIcon(new L.Icon({ iconUrl: stairs, iconSize: [40*Math.pow(2, map.getZoom() - 4), 40*Math.pow(2, map.getZoom() - 4)]}))}); return {body} } function Lift({position, size, body}) { // React class (in function form) to represent lifts, takes the position where to put the lift logo // and the body of the popup of the lift (list of floors accessible from that lift) const map = useMap(); const [markerIcon, setMarkerIcon] = useState(new L.Icon({ iconUrl: lift, iconSize: size })); useMapEvent('zoomend', () => {setMarkerIcon(new L.Icon({ iconUrl: lift, iconSize: [20*Math.pow(2, map.getZoom() - 4), 20*Math.pow(2, map.getZoom() - 4)]}))}); return {body} } function Toilet({position, size}) { // React class (in function form) to represent lifts, takes the position where to put the lift logo // and the body of the popup of the lift (list of floors accessible from that lift) const map = useMap(); const [markerIcon, setMarkerIcon] = useState(new L.Icon({ iconUrl: toilet, iconSize: size })); useMapEvent('zoomend', () => {setMarkerIcon(new L.Icon({ iconUrl: toilet, iconSize: [20*Math.pow(2, map.getZoom() - 4), 20*Math.pow(2, map.getZoom() - 4)]}))}); return } function polygonCenter(polygon) { // Function to calculate the central point of a given polygon let minX = -1; let minY = -1; let maxX = -1; let maxY = -1; polygon.forEach((pt) => { if (minX === -1 || minX > pt[0]) { minX = pt[0]; } if (minY === -1 || minY > pt[1]) { minY = pt[1]; } if (maxY === -1 || maxY < pt[1]) { maxY = pt[1]; } if (maxX === -1 || maxX < pt[0]) { maxX = pt[0]; } }) return [(minX + maxX) / 2, (maxY + minY) / 2]; } function logoSize(polygon) { // function to calculate the size of a logo to display // on a room which edges are in polygon let minX = -1; let minY = -1; let maxX = -1; let maxY = -1; // We first calculate the maximal coordinates to then output // 75% the size of the room polygon.forEach((pt) => { if (minX === -1 || minX > pt[0]) { minX = pt[0]; } if (minY === -1 || minY > pt[1]) { minY = pt[1]; } if (maxY === -1 || maxY < pt[1]) { maxY = pt[1]; } if (maxX === -1 || maxX < pt[0]) { maxX = pt[0]; } }) // For some unknown reason we have to multiply by 10 otherwise // it's almost invisible const x = 30*(maxX - minX)/4; const y = 30*(maxY - minY)/4; // We return a square size to make it better looking so we // take the minimal size of border if (x < y) { return [x,x]; } return [y,y]; } function min_max(request) { // Function to calculate the minimal and maximal coordinates of all the points in a request // (it being a list of polygons, a polygon being a list of points, a point being a list of 2 coordinates) let minX = -1; let minY = -1; let maxX = -1; let maxY = -1; for (const ind in request) { const element = request[ind]; // element can be a room, a lift, a staircase or toilets (element["surface"]).forEach((pt) => { if (minX === -1 || minX > pt["x"]) { minX = pt["x"]; } if (minY === -1 || minY > pt["y"]) { minY = pt["y"]; } if (maxY === -1 || maxY < pt["y"]) { maxY = pt["y"]; } if (maxX === -1 || maxX < pt["x"]) { maxX = pt["x"]; } }) } return [minX, maxX, minY, maxY]; } function floorList(connectedFloors, callbackChangeFloor) { // Returns under html format a list of floors connected // listed in connectedFloors let htmlFloorList = []; for (const floor in connectedFloors) { htmlFloorList = [...htmlFloorList,
  • {callbackChangeFloor(connectedFloors[floor]["id"]);}}> {connectedFloors[floor]["name"]}
  • ]; } return htmlFloorList; } function newPolygon(element, positions, selectedRoom, callbackRoomSelected, callbackChangeFloor) { // Auxiliary function to return the appropriate polygon depending // on wether it is a room, a lift, a staircase or toilets const color = selectedRoom === element["id"] ? 'red' : 'grey'; if (element["type"] === "S") { // In this case, the room is a staircase so we add a Stairs component return callbackRoomSelected(element["id"])}}> {element["id"]} {floorList(element["connectedFloors"], callbackChangeFloor)} } /> } else if (element["type"] === "L") { // In this case the room is a lift so we add a Lift component return callbackRoomSelected(element["id"])}}> {element["id"]} {floorList(element["connectedFloors"], callbackChangeFloor)} } /> } else if (element["type"] === "T") { return callbackRoomSelected(element["id"])}}> {element["id"]} } else if (element["type"] === "R") { // In this case it is a regular room return callbackRoomSelected(element["id"])}}> {element["id"]} } else if (element["type"] === "C") { // In this case it is a corridor return {element["id"]} } } function list_polygons(request, center, callbackRoomSelected, selectedRoom, callbackChangeFloor) { // Main function to calculate the list of polygons (React components) // Taking the request as parameter, the center of the points of the request // and a callback (callback) to interact with the global web interface // when a room is clicked (allowing the display of room informations) let polygons = []; for (const ind in request) { const element = request[ind]; // element can be a room, a lift, a staircase or toilets const positions = (element["surface"]).map((pt) => { // This function rotates and recenters de the map (should not be necessary once the real data are gathered and // only works for third floor) // It also changes the objects into lists of two coordinates to make it accepted by the "positions" attribute of // the "Polygon" react-leaflet component return [-(pt["y"] - center[1]) / 6, (pt["x"] - center[0]) / 6]; }); const pol = newPolygon(element, positions, selectedRoom, callbackRoomSelected, callbackChangeFloor); polygons = [...polygons, pol]; } return polygons; } function buildPols(request, callbackRoomSelected, selectedRoom, callbackChangeFloor) { // This function just calls the list_polygons one with appropriate parameters (precalculate center) const minMaxXY = min_max(request); const minX = minMaxXY[0]; const maxX = minMaxXY[1]; const minY = minMaxXY[2]; const maxY = minMaxXY[3]; const center = [(maxX + minX) / 2, (maxY + minY) / 2]; const polygons = list_polygons(request, center, callbackRoomSelected, selectedRoom, callbackChangeFloor); return polygons; } function Map({callbackRoomSelected, selectedRoom, floorID, callbackChangeFloor}) { // When the user selects a room on the map, call callbackRoomSelected. // The room that is currently selected is selectedRoom. It is null if no room // is selected const [loading, setLoading] = useState(true); const [floor, setFloor] = useState(null); const [error, setError] = useState(null); // We use the useEffect hook to fetch the data to build the Map useEffect(() => { fetch("https://encartes.aliens-lyon.fr/api/map/get_floor/"+floorID) .then(response => response.json()) .then(data => {setLoading(false); setFloor(data)}) .catch((error) => {setError("API unreachable");}); }, [floorID]); // We distinguish wether to print an error, a loading message // or the map if we got the answer if (error != null) { return {error} ; } else if (loading) { return Loading map ; } else { const request = floor["places"]; /*const createPolygons = React.useCallback((request, callbackRoomSelected, selectedRoom) => { return buildPols(request, callbackRoomSelected, selectedRoom); }, [request, callbackRoomSelected, selectedRoom]); const polygons = createPolygons(request, callbackRoomSelected, selectedRoom); */ const polygons = buildPols(request, callbackRoomSelected, selectedRoom, callbackChangeFloor); return {polygons} } } export default Map;