-
Notifications
You must be signed in to change notification settings - Fork 36
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
Add kml selection exports #5300
base: production
Are you sure you want to change the base?
Changes from 4 commits
3296e85
62a3e6f
d5e518e
e0c7885
4390fcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,9 @@ import { QueryButton } from './Components'; | |
import type { QueryField } from './helpers'; | ||
import { hasLocalityColumns } from './helpers'; | ||
import type { QueryResultRow } from './Results'; | ||
import { format } from '../Formatters/formatters'; | ||
import { jsonToXml, XmlNode, SimpleXmlNode } from '../Syncer/xmlToJson'; | ||
import { downloadFile } from '../Molecules/FilePicker'; | ||
|
||
export function QueryExportButtons({ | ||
baseTableName, | ||
|
@@ -89,16 +92,23 @@ export function QueryExportButtons({ | |
'exportCsvUtf8Bom' | ||
); | ||
|
||
function formatExportFileName( | ||
file_extension: string | ||
): string { | ||
return `${ | ||
queryResource.isNew() | ||
? `${queryText.newQueryName()} ${genericTables[baseTableName].label}` | ||
: queryResource.get('name') | ||
} - ${new Date().toDateString()}.${file_extension}`; | ||
} | ||
|
||
/* | ||
*Will be only called if query is not distinct, | ||
*selection not enabled when distinct selected | ||
*/ | ||
async function exportSelected(): Promise<void> { | ||
const name = `${ | ||
queryResource.isNew() | ||
? `${queryText.newQueryName()} ${genericTables[baseTableName].label}` | ||
: queryResource.get('name') | ||
} - ${new Date().toDateString()}.csv`; | ||
|
||
async function exportCsvSelected(): Promise<void> { | ||
const name = formatExportFileName('csv'); | ||
|
||
const selectedResults = results?.current?.map((row) => | ||
row !== undefined && f.has(selectedRows, row[0]) | ||
|
@@ -125,6 +135,148 @@ export function QueryExportButtons({ | |
); | ||
} | ||
|
||
async function exportKmlSelected(): Promise<void> { | ||
const name = formatExportFileName('kml'); | ||
|
||
const selectedResults = results?.current?.map((row) => | ||
row !== undefined && f.has(selectedRows, row[0]) | ||
? row?.slice(1).map((cell) => cell?.toString() ?? '') | ||
: undefined | ||
); | ||
|
||
if (selectedResults === undefined) return undefined; | ||
|
||
const filteredResults = filterArray(selectedResults); | ||
|
||
const columnsName = fields | ||
.filter((field) => field.isDisplay) | ||
.map((field) => | ||
generateMappingPathPreview(baseTableName, field.mappingPath) | ||
); | ||
|
||
|
||
let placemarkTarget: any = []; | ||
|
||
filteredResults?.forEach((result: any) => { | ||
let dataTarget: any = []; | ||
|
||
// <ExtendedData> | ||
let extendedDataTarget: any = []; | ||
|
||
fields.forEach((field, index) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this could be implemented using see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map that would match the code style in the rest of the codebase and reduce the need for explicitly specifying typescript types |
||
const fieldValue = result?.[index + 1]; | ||
|
||
extendedDataTarget.push({ | ||
tagName: "Data", | ||
attributes: { name: columnsName[index + 1] }, | ||
children: [ | ||
{ | ||
tagName: "value", | ||
attributes: {}, | ||
children: [ | ||
{ | ||
tagName: "value", | ||
attributes: {}, | ||
children: [], | ||
type: "Text", | ||
string: String(fieldValue) | ||
} | ||
], | ||
} | ||
] | ||
}); | ||
}); | ||
|
||
let extendedData: XmlNode = { | ||
tagName: "ExtendedData", | ||
attributes: {}, | ||
children: extendedDataTarget as ReadonlyArray<XmlNode> | ||
}; | ||
|
||
// push | ||
dataTarget.push(extendedData); | ||
|
||
// <name> | ||
const nameValue = fields.map((field) => result?.[field.id]).join(' - '); | ||
let nameData: XmlNode = { | ||
tagName: "name", | ||
attributes: {}, | ||
children: [ | ||
{ | ||
tagName: "name", | ||
attributes: {}, | ||
children: [], | ||
type: "Text", | ||
string: nameValue | ||
} | ||
], | ||
}; | ||
// push | ||
dataTarget.push(nameData); | ||
|
||
// <Point> | ||
const coordinatesValue = fields | ||
.filter( | ||
(field) => | ||
field.mappingPath.toString().includes('latitude') || | ||
field.mappingPath.toString().includes('longitude') | ||
) | ||
.map((field) => result?.[field.id]) | ||
.join(', '); | ||
|
||
let pointData: XmlNode = { | ||
tagName: "Point", | ||
attributes: {}, | ||
children: [ | ||
{ | ||
tagName: "coordinates", | ||
attributes: {}, | ||
children: [ | ||
{ | ||
tagName: "coordinates", | ||
attributes: {}, | ||
children: [], | ||
type: "Text", | ||
string: coordinatesValue | ||
} | ||
], | ||
} | ||
] | ||
}; | ||
// push | ||
dataTarget.push(pointData); | ||
|
||
let placemark: XmlNode = { | ||
tagName: "Placemark", | ||
attributes: {}, | ||
children: dataTarget as ReadonlyArray<XmlNode> | ||
}; | ||
|
||
// Insert placemark into document (target) | ||
placemarkTarget.push(placemark); | ||
}); | ||
|
||
let jsonData: XmlNode = { | ||
tagName: "kml", | ||
attributes: { | ||
xmlns: "http://earth.google.com/kml/2.2" | ||
}, | ||
children: [ | ||
{ | ||
tagName: "Document", | ||
attributes: {}, | ||
children: placemarkTarget as ReadonlyArray<XmlNode> | ||
} | ||
] | ||
}; | ||
|
||
const xmlElement = jsonToXml(jsonData); | ||
const serializer = new XMLSerializer(); | ||
const xmlString = '<?xml version="1.0" encoding="utf-8"?>\n' + serializer.serializeToString(xmlElement); | ||
|
||
return downloadFile(name, xmlString); | ||
} | ||
|
||
const containsResults = results.current?.some((row) => row !== undefined); | ||
|
||
const canUseKml = | ||
|
@@ -159,7 +311,7 @@ export function QueryExportButtons({ | |
onClick={(): void => { | ||
selectedRows.size === 0 | ||
? doQueryExport('/stored_query/exportcsv/', separator, utf8Bom) | ||
: exportSelected().catch(softFail); | ||
: exportCsvSelected().catch(softFail); | ||
}} | ||
> | ||
{queryText.createCsv()} | ||
|
@@ -171,7 +323,11 @@ export function QueryExportButtons({ | |
showConfirmation={showConfirmation} | ||
onClick={(): void => | ||
hasLocalityColumns(fields) | ||
? doQueryExport('/stored_query/exportkml/', undefined, undefined) | ||
? ( | ||
selectedRows.size === 0 | ||
? doQueryExport('/stored_query/exportkml/', undefined, undefined) | ||
: exportKmlSelected().catch(softFail) | ||
) | ||
: setState('warning') | ||
} | ||
> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,7 +50,7 @@ export const xmlToJson = (element: Element): XmlNode => ({ | |
/** | ||
* Reverse conversion to JSON | ||
*/ | ||
export function jsonToXml(node: XmlNode): Element { | ||
export function jsonToXml(node: any): Element { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please refrain from using you can read a quick reference guide on TypeScript if you wish to be more familiar with TypeScript concepts: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html if you are using VS Code, you can also install this extension to make the error messages more readable: https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha, I didn't think my code would be reviewed yet 😅 I promise this was a temporary hack as I was still figuring out the xml types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that's my mistake, I didn't mean to trigger a code review. Nevertheless, these resources by Max are good starting points! |
||
const xmlDocument = document.implementation.createDocument(null, null); | ||
const element = xmlDocument.createElement(node.tagName); | ||
xmlDocument.append(element); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reduce usages of
any
.in this case the type
XmlNode
might be better