Skip to content

Commit

Permalink
Merge pull request #28 from varun2948/feat-osm-download
Browse files Browse the repository at this point in the history
Feat osm download
  • Loading branch information
royallsilwallz authored Dec 15, 2023
2 parents 6e5809c + 71f1184 commit 07453a3
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 0 deletions.
5 changes: 5 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,8 @@ TM_DEFAULT_LOCALE=en
# Sentry.io DSN Config (optional)
# TM_SENTRY_BACKEND_DSN=https://foo.ingest.sentry.io/1234567
# TM_SENTRY_FRONTEND_DSN=https://bar.ingest.sentry.io/8901234


EXPORT TOOL Integration with 0(Disable) and 1(Enable) and S3 URL for Export Tool
#EXPORT_TOOL_S3_URL=https://foorawdataapi.s3.amazonaws.com
#ENABLE_EXPORT_TOOL=0
1 change: 1 addition & 0 deletions frontend/.env.expand
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ REACT_APP_SENTRY_FRONTEND_DSN=$TM_SENTRY_FRONTEND_DSN
REACT_APP_ENVIRONMENT=$TM_ENVIRONMENT
REACT_APP_TM_DEFAULT_CHANGESET_COMMENT=$TM_DEFAULT_CHANGESET_COMMENT
REACT_APP_RAPID_EDITOR_URL=$RAPID_EDITOR_URL
REACT_APP_EXPORT_TOOL_S3_URL=$EXPORT_TOOL_S3_URL
148 changes: 148 additions & 0 deletions frontend/src/components/projectDetail/downloadOsmData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { useState } from 'react';
import { RoadIcon, HomeIcon, WavesIcon, TaskIcon, AsteriskIcon } from '../svgIcons';
import FileFormatCard from './fileFormatCard';
import Popup from 'reactjs-popup';
import { EXPORT_TOOL_S3_URL } from '../../config';

export const TITLED_ICONS = [
{ Icon: RoadIcon, title: 'roads', value: 'ROADS' },
{ Icon: HomeIcon, title: 'buildings', value: 'BUILDINGS' },
{ Icon: WavesIcon, title: 'waterways', value: 'WATERWAYS' },
{ Icon: TaskIcon, title: 'landUse', value: 'LAND_USE' },
{ Icon: AsteriskIcon, title: 'other', value: 'OTHER' },
];

const fileFormats = [{ format: 'SHP' }, { format: 'GEOJSON' }, { format: 'KML' }];

/**
* Renders a list of download options for OSM data based on the project mapping types.
*
* @param {Array<string>} projectMappingTypes - The mapping types of the project.
* @return {JSX.Element} - The JSX element containing the download options.
*/

export const DownloadOsmData = ({ projectMappingTypes, project }) => {
const [showPopup, setShowPopup] = useState(false);
const [isDownloadingState, setIsDownloadingState] = useState(null);

/**
* Downloads an S3 file from the given URL and saves it as a file.
*
* @param {string} title - The title of the file.
* @param {string} fileFormat - The format of the file.
* @return {Promise<void>} Promise that resolves when the download is complete.
*/
const downloadS3File = async (title, fileFormat) => {
// Create the base URL for the S3 file
const baseUrl = `${EXPORT_TOOL_S3_URL}/TM/${project.projectId}/hotosm_project_${
project.projectId
}_${title}_${fileFormat?.toLowerCase()}.zip`;

// Set the state to indicate that the file download is in progress
setIsDownloadingState({ title: title, fileFormat: fileFormat, isDownloading: true });

try {
// Fetch the file from the S3 URL
const response = await fetch(baseUrl);

// Check if the request was successful
if (response.ok) {
// Set the state to indicate that the file download is complete
setIsDownloadingState({ title: title, fileFormat: fileFormat, isDownloading: false });

// Get the file data as a blob
const blob = await response.blob();

// Create a download link for the file
const href = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = href;
link.setAttribute(
'download',
`hotosm_project_${project.projectId}_${title}_${fileFormat?.toLowerCase()}.zip`,
);

// Add the link to the document body, click it, and then remove it
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
// Show a popup and throw an error if the request was not successful
setShowPopup(true);
throw new Error(`Request failed with status: ${response.status}`);
}
} catch (error) {
// Show a popup and log the error if an error occurs during the download
setShowPopup(true);
setIsDownloadingState({ title: title, fileFormat: fileFormat, isDownloading: false });
console.error('Error:', error.message);
}
};
const filteredMappingTypes = TITLED_ICONS?.filter((icon) =>
projectMappingTypes?.includes(icon.value),
);

return (
<div className="mb5 w-100 pa5 ph flex flex-wrap">
<Popup modal open={showPopup} closeOnDocumentClick nested onClose={() => setShowPopup(false)}>
{(close) => (
<div class="blue-dark bg-white pv2 pv4-ns ph2 ph4-ns">
<h3 class="barlow-condensed f3 fw6 mv0">Data Not Available.</h3>
<div class="w-100 pt3 flex justify-end">
<button
aria-pressed="false"
focusindex="0"
class="mr2 bg-red white br1 f5 bn pointer"
style={{ padding: '0.75rem 1.5rem' }}
onClick={() => {
setShowPopup(false);
close();
}}
>
Close
</button>
</div>
</div>
)}
</Popup>
{filteredMappingTypes.map((type) => (
<div
className="osm-card bg-white pa3 mr4 mt4 w-auto-m flex flex-wrap items-center "
style={{
width: '560px',
gap: '16px',
}}
key={type.title}
>
<div
style={{
justifyContent: 'center',
display: 'flex',
alignItems: 'center',
}}
>
<type.Icon
title={type.title}
color="#D73F3F"
className="br1 h2 w2 pa1 ma1 ba b--white bw1 dib h-65 w-65"
style={{ height: '56px' }}
/>
</div>

<div
className="file-list flex barlow-condensed f3"
style={{ display: 'flex', gap: '12px' }}
>
<p className="fw5 ttc">{type.title}</p>
<FileFormatCard
title={type.title}
fileFormats={fileFormats}
downloadS3Data={downloadS3File}
isDownloadingState={isDownloadingState}
/>
</div>
</div>
))}
</div>
);
};
40 changes: 40 additions & 0 deletions frontend/src/components/projectDetail/fileFormatCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { AnimatedLoadingIcon } from '../button';

/**
* Renders a list of file formats as clickable links.
*
* @param {Object[]} fileFormats - An array of file format objects.
* @param {string} fileFormats[].format - The format of the file.
* @return {JSX.Element} The rendered list of file formats.
*/

function FileFormatCard({ title, fileFormats, downloadS3Data, isDownloadingState }) {
return (
<>
{fileFormats.map((fileFormat, index) => (
<React.Fragment key={index}>
<div
tabIndex={0}
style={{ cursor: 'pointer' }}
role="button"
onClick={() => downloadS3Data(title, fileFormat.format)}
className="link hover-red color-inherit"
>
<p className="underline fw5" style={{ textUnderlineOffset: '5px' }}>
{fileFormat.format}
{isDownloadingState?.isDownloading &&
isDownloadingState?.title === title &&
isDownloadingState?.fileFormat === fileFormat?.format ? (
<AnimatedLoadingIcon />
) : null}
</p>
</div>
{index !== fileFormats.length - 1 && <hr className="file-list-separator" />}
</React.Fragment>
))}
</>
);
}

export default FileFormatCard;
19 changes: 19 additions & 0 deletions frontend/src/components/projectDetail/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Alert } from '../alert';

import './styles.scss';
import { useWindowSize } from '../../hooks/UseWindowSize';
import { DownloadOsmData } from './downloadOsmData.js';

/* lazy imports must be last import */
const ProjectTimeline = React.lazy(() => import('./timeline' /* webpackChunkName: "timeline" */));
Expand Down Expand Up @@ -285,6 +286,24 @@ export const ProjectDetail = (props) => {
/>
)}
</div>

{/* Download OSM Data section Start */}

<div className="bg-tan-dim">
<a href="#downloadOsmData" name="downloadOsmData" style={{ visibility: 'hidden' }}>
<FormattedMessage {...messages.downloadOsmData} />
</a>
<h3 className={`${h2Classes}`}>
<FormattedMessage {...messages.downloadOsmData} />
</h3>
<DownloadOsmData
projectMappingTypes={props?.project?.mappingTypes}
project={props.project}
/>
</div>

{/* Download OSM Data section End */}

<a href="#contributionTimeline" style={{ visibility: 'hidden' }} name="contributionTimeline">
<FormattedMessage {...messages.contributionsTimeline} />
</a>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/components/projectDetail/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ export default defineMessages({
id: 'project.detail.sections.contributionsTimeline',
defaultMessage: 'Contributions timeline',
},
downloadOsmData: {
id: 'project.detail.sections.downloadOsmData',
defaultMessage: 'Download OSM Data',
},
viewInOsmcha: {
id: 'project.detail.sections.contributions.osmcha',
defaultMessage: 'Changesets in OSMCha',
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/components/projectDetail/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,10 @@
.menu-items-container {
scrollbar-width: none;
}

.file-list-separator {
top: 13px;
height: 27px;
position: relative;
border: 1px solid #d73f3f;
}
1 change: 1 addition & 0 deletions frontend/src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const POTLATCH2_EDITOR_URL =
'https://www.openstreetmap.org/edit?editor=potlatch2';
export const RAPID_EDITOR_URL =
process.env.REACT_APP_RAPID_EDITOR_URL || 'https://mapwith.ai/rapid';
export const EXPORT_TOOL_S3_URL = process.env.REACT_APP_EXPORT_TOOL_S3_URL || '';

export const TASK_COLOURS = {
READY: '#fff',
Expand Down

0 comments on commit 07453a3

Please sign in to comment.