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

Feat osm download #6176

Merged
merged 36 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
31f7c11
feat(osm-download): Component Configured
varun2948 Dec 8, 2023
a1c9c48
feat(osm-download): fileformatcard component
varun2948 Dec 8, 2023
38ffca7
feat(osm-download): project detail osm download card integrate
varun2948 Dec 8, 2023
6e3e052
feat(osm-download): download osm data title
varun2948 Dec 8, 2023
6899e9a
feat(osm-download-style): style.scss updated
varun2948 Dec 8, 2023
9208942
feat: added project metadata into component props
varun2948 Dec 15, 2023
1e5e54e
feat: added config for export tool s3 url
varun2948 Dec 15, 2023
364118a
feat: added export tool s3 url and enable env
varun2948 Dec 15, 2023
8ddeb67
feat: refactored code and added popup for not available data
varun2948 Dec 15, 2023
71f1184
feat: added spinner for loading on download
varun2948 Dec 15, 2023
a662493
fix: json of mapping type changed
varun2948 Dec 19, 2023
7b5ff61
revert: changes revert on mapping types
varun2948 Dec 19, 2023
f74b436
feat: added formatted message and handled loading after download
varun2948 Dec 22, 2023
e4fcaf0
feat: toggle mode changed
varun2948 Dec 22, 2023
b910488
feat: loadingstate refactored
varun2948 Dec 22, 2023
9469377
feat: messages json maintened
varun2948 Dec 22, 2023
22d79a0
feat: conditional export tool feature added
varun2948 Dec 22, 2023
080da1c
feat: added env bool for enable export tool
varun2948 Dec 22, 2023
6d812ed
feat: config added
varun2948 Dec 22, 2023
15ec239
fix (sonarLint): classname and proptypes issue
varun2948 Dec 27, 2023
ddd9739
fix(SonarLint) : fileFormatCard proptypes
varun2948 Dec 27, 2023
8ff0499
Feat project details proptypes added sonarlint fix
varun2948 Dec 28, 2023
8f5129a
Feat removed button tag for ui representation
varun2948 Dec 28, 2023
645f8e9
Feat added footer menuitem for downloadosmdata
varun2948 Dec 28, 2023
c12a696
Fix order of menuitem fixed
varun2948 Dec 28, 2023
eb859f4
feat animation scss added
varun2948 Jan 4, 2024
7d6c0a2
feat download svg icon added
varun2948 Jan 4, 2024
da7aadb
feat UI added for category of formats
varun2948 Jan 4, 2024
bca69c7
feat format category hover effect added
varun2948 Jan 4, 2024
bbb367b
feat svg icon added on index of svgicons
varun2948 Jan 4, 2024
f83f0c5
fix sonarlint changes
varun2948 Jan 4, 2024
e65c57f
feat: added feature_type on downloaded file
varun2948 Jan 4, 2024
80a516b
feat removed points from buildings
varun2948 Jan 4, 2024
574770a
feat: hover effect added on osm download icon
varun2948 Jan 5, 2024
a38dc57
feat padding reduced for gaps on osmdownload
varun2948 Jan 5, 2024
838eb5b
feat: format handled from config itself
varun2948 Jan 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 2 additions & 0 deletions frontend/.env.expand
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ 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
REACT_APP_ENABLE_EXPORT_TOOL=$ENABLE_EXPORT_TOOL
14 changes: 14 additions & 0 deletions frontend/src/assets/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@
@import 'learn';
@import 'notifications';
@import 'contributions';

.fade-in {
opacity: 0;
transition: opacity 0.5s ease-in;
}

.fade-in.active {
opacity: 1;
}

.categorycard:hover > svg *,
.categorycard:hover {
fill: #d73f3f;
}
231 changes: 231 additions & 0 deletions frontend/src/components/projectDetail/downloadOsmData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { RoadIcon, HomeIcon, WavesIcon, TaskIcon, DownloadIcon } from '../svgIcons';
import FileFormatCard from './fileFormatCard';
import Popup from 'reactjs-popup';
import { EXPORT_TOOL_S3_URL } from '../../config';
import messages from './messages';
import { FormattedMessage } from 'react-intl';

export const TITLED_ICONS = [
{
Icon: RoadIcon,
title: 'roads',
value: 'ROADS',
featuretype: ['lines'],
formats: ['geojson', 'shp', 'kml'],
},
{
Icon: HomeIcon,
title: 'buildings',
value: 'BUILDINGS',
featuretype: ['polygons'],
formats: ['geojson', 'shp', 'kml'],
},
{
Icon: WavesIcon,
title: 'waterways',
value: 'WATERWAYS',
featuretype: ['lines', 'polygons'],
formats: ['geojson', 'shp', 'kml'],
},
{
Icon: TaskIcon,
title: 'landuse',
value: 'LAND_USE',
featuretype: ['points', 'polygons'],
formats: ['geojson', 'shp', '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);
const [selectedCategoryFormat, setSelectedCategoryFormat] = useState(null);

const datasetConfig = {
dataset_prefix: `hotosm_project_${project.projectId}`,
dataset_folder: 'TM',
dataset_title: `Tasking Manger Project ${project.projectId}`,
};
/**
* 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.
* @param {string} feature_type - The feature type of the ffile.
* @return {Promise<void>} Promise that resolves when the download is complete.
*/
const downloadS3File = async (title, fileFormat, feature_type) => {
// Create the base URL for the S3 file
const baseUrl = `${EXPORT_TOOL_S3_URL}/${datasetConfig.dataset_folder}/${
datasetConfig.dataset_prefix
}/${title}/${feature_type}/${
datasetConfig.dataset_prefix
}_${title}_${feature_type}_${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) {
// 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}_${feature_type}_${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);
// Set the state to indicate that the file download is complete
setIsDownloadingState({ title: title, fileFormat: fileFormat, isDownloading: false });
} 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 pb5 ph4 flex flex-wrap">
<Popup modal open={showPopup} closeOnDocumentClick nested onClose={() => setShowPopup(false)}>
{(close) => (
<div className="blue-dark bg-white pv2 pv4-ns ph2 ph4-ns">
<h3 className="barlow-condensed f3 fw6 mv0">
<FormattedMessage {...messages.errorDownloadOsmData} />
</h3>
<p className="mt4">
<FormattedMessage {...messages.errorDownloadOsmDataDescription} />
</p>
<div className="w-100 pt3 flex justify-end">
<button
aria-pressed="false"
tabIndex={0}
className="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="flex-column">
<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={type.formats}
downloadS3Data={downloadS3File}
isDownloadingState={isDownloadingState}
selectedCategoryFormat={selectedCategoryFormat}
setSelectedCategoryFormat={setSelectedCategoryFormat}
/>
</div>
<div
className={`flex flex-row ${
selectedCategoryFormat && selectedCategoryFormat.title === type.title
? 'fade-in active'
: 'fade-in'
} `}
style={{ gap: '20px' }}
>
{selectedCategoryFormat &&
selectedCategoryFormat.title === type.title &&
type?.featuretype?.map((typ) => (
<span
key={`${typ}_${selectedCategoryFormat.title}`}
onClick={() =>
downloadS3File(
selectedCategoryFormat.title,
selectedCategoryFormat.format,
typ,
)
}
onKeyUp={() =>
downloadS3File(
selectedCategoryFormat.title,
selectedCategoryFormat.format,
typ,
)
}
className="flex flex-row items-center pointer link hover-red color-inherit categorycard"
style={{ gap: '10px' }}
>
<DownloadIcon style={{ height: '28px' }} color="#D73F3F" />
<p className="ttc">
{typ} {selectedCategoryFormat.format}
</p>
</span>
))}
</div>
</div>
</div>
))}
</div>
);
};

DownloadOsmData.propTypes = {
projectMappingTypes: PropTypes.arrayOf(PropTypes.string).isRequired,
project: PropTypes.objectOf(PropTypes.any).isRequired,
};
73 changes: 73 additions & 0 deletions frontend/src/components/projectDetail/fileFormatCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { AnimatedLoadingIcon } from '../button';
import PropTypes from 'prop-types';

/**
* Renders a list of file formats as clickable links.
*
* @param {string} props.title - The title of the card.
* @param {Object[]} fileFormats - An array of file format objects.
* @param {Object} isDownloadingState - The downloading state object.
* @param {function} setSelectedCategoryFormat - The function to set the selected category format.
* @param {Object} selectedCategoryFormat - The selected category format object.
* @return {JSX.Element} The rendered list of file formats.
*/

function FileFormatCard({
title,
fileFormats,
isDownloadingState,
setSelectedCategoryFormat,
selectedCategoryFormat,
}) {
return (
<>
{fileFormats.map((fileFormat, index) => {
const loadingState =
isDownloadingState?.isDownloading &&
isDownloadingState?.title === title &&
isDownloadingState?.fileFormat === fileFormat;

return (
<React.Fragment key={fileFormat}>
<span
role="button"
tabIndex={0}
style={
loadingState
? { cursor: 'not-allowed', pointerEvents: 'none' }
: { cursor: 'pointer' }
}
onClick={() => setSelectedCategoryFormat({ title, format: fileFormat })}
onKeyUp={() => setSelectedCategoryFormat({ title, format: fileFormat })}
className={`link ${
selectedCategoryFormat === fileFormat && selectedCategoryFormat.title === title
? 'red'
: ''
} hover-red color-inherit`}
>
<p className="underline fw5 ttu " style={{ textUnderlineOffset: '5px' }}>
{fileFormat}
{loadingState ? <AnimatedLoadingIcon /> : null}
</p>
</span>
{index !== fileFormats.length - 1 && <hr className="file-list-separator" />}
</React.Fragment>
);
})}
</>
);
}

export default FileFormatCard;

FileFormatCard.propTypes = {
title: PropTypes.string,
fileFormats: PropTypes.arrayOf(PropTypes.object),
isDownloadingState: PropTypes.bool,
setSelectedCategoryFormat: PropTypes.func,
selectedCategoryFormat: PropTypes.objectOf({
title: PropTypes.string,
format: PropTypes.PropTypes.string,
}),
};
4 changes: 4 additions & 0 deletions frontend/src/components/projectDetail/footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ const menuItems = [
href: '#contributions',
label: <FormattedMessage {...messages.contributions} />,
},
{
href: '#downloadOsmData',
label: <FormattedMessage {...messages.downloadOsmData} />,
},
{
href: '#similarProjects',
label: <FormattedMessage {...messages.similarProjects} />,
Expand Down
Loading
Loading