Skip to content

Commit

Permalink
Geolocation page (#271)
Browse files Browse the repository at this point in the history
* Adding map and geolocate features

* Adding files to create geolocation feature

* Adding the full url of the fetch
  • Loading branch information
sylviamclaughlin authored Sep 25, 2023
1 parent 87be22c commit 3ce68e5
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 22 deletions.
181 changes: 168 additions & 13 deletions frontend/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 frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
"formik": "^2.4.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-leaflet": "^4.2.1",
"react-pro-sidebar": "^0.7.1",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-simple-maps": "^3.0.0",
"recharts": "^2.8.0",
"web-vitals": "^2.1.4",
"yup": "^1.2.0"
Expand Down
122 changes: 113 additions & 9 deletions frontend/src/scenes/geolocate/index.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,118 @@
import { Box } from "@mui/material";
import { Box, Alert } from "@mui/material";
import Header from "../../components/Header";
import { React, useState } from "react";
import { ComposableMap, Geographies, Geography } from "react-simple-maps"
import { Marker } from "react-simple-maps"
import Paper from '@mui/material/Paper';
import InputBase from '@mui/material/InputBase';
import IconButton from '@mui/material/IconButton';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import SearchIcon from '@mui/icons-material/Search';
import PinDropIcon from '@mui/icons-material/PinDrop';

// Return a geolocate component. Right now it just displays the title and subtitle.
const Geolocate = () => {
//set the maps' width and height cooordinates
const width = 800
const height = 500
const geoUrl =
"https://raw.githubusercontent.com/deldersveld/topojson/master/world-countries.json"


// call the backend API to fetch the location data using the /geolocate endpoint
// if we are in development we need to call the API using the full URL
function fetchLocationData(inputValue) {
if (process.env.REACT_APP_ENVIRONMENT === 'dev') {
return fetch(process.env.REACT_APP_API_URL + '/geolocate/' + inputValue);
}
else {
return fetch('/geolocate' + inputValue);
}
}

function Geolocate() {
const [inputValue, setInputValue] = useState('');
const [location, setLocation] = useState(null);

//execute the fetchLocationData function when the user clicks on the search button or presses enter
function handleGeolocateSearch(e) {
e.preventDefault();
//call the api to fetch the location data
fetchLocationData(inputValue).then(response => {
if (!response.ok) {
throw new Error('Invalid IP address');
}
return response.json();
})
.then(data => {
// Handle the JSON data from the response
setLocation(data);
})
.catch(error => {
setLocation({detail: "Something went wrong: " + error});
console.error('There was a problem with the fetch operation:', error);
});
};

// execute the handleGeolocateSearch function when the user presses enter
function HandleKeyDown(e) {
if (e.key === 'Enter') {
handleGeolocateSearch(e);
}
}

return (
<Box m="20px">
<Box display="flex" justifyContent="space-between" alignItems="center">
<Header title="Geolocate an IP" subtitle="Geolocate a particular ip address"/>
</Box>
</Box>
)};

{/* Display the title */}
<Box display="flex" justifyContent="space-between" alignItems="center">
<Header title="Geolocate an IP"/>
</Box>
{/* Display the input bo and search button */}
<Box>
<Paper
sx={{ p: '2px 4px', display: 'flex', alignItems: 'center', width: 400 }}
>
<IconButton sx={{ p: '10px' }} aria-label="menu">
<PinDropIcon/>
{/* <MenuIcon /> */}
</IconButton>
<InputBase
sx={{ ml: 1, flex: 1 }}
placeholder="Enter an IP Address to geolocate"
inputProps={{ 'aria-label': 'search ip' }}
onChange={e => setInputValue(e.target.value)}
onKeyDown={e => HandleKeyDown(e)}
/>
<IconButton type="button" sx={{ p: '10px' }} aria-label="search" onClick={handleGeolocateSearch}>
<SearchIcon />
</IconButton>
</Paper>
{/* If we have data, then display the city, country and location coordinates. If not, then display the error alert */}
{location && location.detail ? (
<Box >
<Alert sx={{fontSize:'larger'}} severity="error">{location.detail}</Alert></Box>
) : (
location && <Box sx={{fontSize:'larger'}}><br></br><CheckCircleOutlineIcon color="success" fontSize="large"/> Located in <b>{location.city}, {location.country}</b> with coordinates [{location.latitude}, {location.longitude}]</Box>)}
{/* Display the map */}
<ComposableMap width={width} height={height}>
<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => (
<Geography key={geo.rsmKey} geography={geo} style={{
hover: { fill: "#adadad" },
pressed: { fill: "#5a5a5a" },
}} />
))
}
</Geographies>
{/* Display the location coordinates of our location with a red circle. */}
{location && location.latitude && location.longitude && (
<Marker coordinates={[location.longitude, location.latitude]}>
<circle r={5} fill="#F53" />
</Marker>
)}
</ComposableMap>
</Box>
</Box>
);
}

export default Geolocate;

0 comments on commit 3ce68e5

Please sign in to comment.