Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 7 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link
rel="icon"
type="image/svg+xml"
href="https://www.landcover100.com/img/logo.png"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>遥感产品数据云</title>
</head>
<body>
<div id="root"></div>
Expand Down
620 changes: 360 additions & 260 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
"preview": "vite preview"
},
"dependencies": {
"@antv/l7": "^2.20.5",
"@antv/larkmap": "^1.4.10",
"@antv/l7": "^2.11.0",
"@antv/l7-draw": "^3.1.0",
"@antv/larkmap": "^1.4.4",
"@turf/turf": "^6.5.0",
"@types/turf": "^3.5.32",
"antd": "^5.12.2",
"dayjs": "^1.11.10",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-router-dom": "^6.21.0"
},
"devDependencies": {
"@types/lodash": "^4.14.202",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@typescript-eslint/eslint-plugin": "^6.14.0",
Expand Down
39 changes: 0 additions & 39 deletions src/App.css

This file was deleted.

50 changes: 0 additions & 50 deletions src/App.tsx

This file was deleted.

5 changes: 5 additions & 0 deletions src/components/data-assets/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.container {
width: 100vw;
height: calc(100vh - 64px);
color: #000;
}
8 changes: 8 additions & 0 deletions src/components/data-assets/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import styles from "./index.module.less"

export const DataAssets = () => {

return (
<div className={styles.container}>数据检索</div>
)
}
32 changes: 32 additions & 0 deletions src/components/data-retrieval/data-panel/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.dataPanel {
height: calc(100vh - 64px);
width: 380px;
color: #000;
padding: 10px;
border-right: 1px solid #e4e1e1;
overflow-y: scroll;

:global {
.ant-tree .ant-tree-node-content-wrapper {
display: flex;
align-items: center;
}

.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected {
background-color: #1677ff;
color: #fff;

.anticon {
color: #fff;
}
}
}
}

.dataPanel {
overflow: auto;
}

.dataPanel::-webkit-scrollbar {
width: 0.5em;
}
150 changes: 150 additions & 0 deletions src/components/data-retrieval/data-panel/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Button, Radio, RadioChangeEvent, Spin, Tree, message } from "antd"
import styles from "./index.module.less"
import { mockTree } from "./mock-tree"
import { useState } from "react"
import { getEnv, setFetchHeader } from "../../../utils"
import { TileData } from "../../../typing"
import dayjs from "dayjs"

type DataPanelProps = {
setSelectDataTile: (value: TileData | undefined) => void;
cityInfo: { path: string, code: string } | undefined
}

const bytesPerDataType: Record<string, number> = {
'byte': 1,
'int16': 2,
'uint16': 2,
'int32': 4,
'uint32': 4,
'float32': 4,
'float64': 8,
'cint16': 4,
'cint32': 8,
'cfloat32': 8,
'cfloat64': 16
};

const PUBLIC_PATH = "/www/wwwroot/landcover100/public"

export const DataPanel = (props: DataPanelProps) => {
const { setSelectDataTile, cityInfo } = props
const [value, setValue] = useState<string>("")
const [loading, setLoading] = useState(false)

const onSelect = (keys: any) => {
if (keys.length === 0) {
setSelectDataTile(undefined)
return
}
const [key] = keys
fetch(
getEnv('/getResourceTree'),
setFetchHeader({ id: key })
)
.then((res) => res.json())
.then((res) => {
setSelectDataTile(res)
}).catch((err) => {
message.error(err)
setSelectDataTile(undefined)
})
}

const onRadioChange = (e: RadioChangeEvent) => {
const newValue = e.target.value
setValue(newValue)
}

const onDownLoadData = async () => {
if (!value) {
message.info("请选择数据源")
return
}
if (!cityInfo?.path) {
message.info("请选择下载区域")
return
}
const vectorDataFilePath = `${PUBLIC_PATH}${cityInfo.path}`
const newValue = value.includes("#") ? value.replace("#", '') : value
setLoading(true)
const [alculateArea, getResourceTree] = await Promise.all([
fetch(getEnv('/calculate_area'), setFetchHeader({ vectorDataFilePath })).then((res) => res.json()),
fetch(getEnv('/getResourceTree'), setFetchHeader({ id: newValue })).then((res) => res.json())
])
const { dataType, resolution, zip_level, resPath } = getResourceTree || {}

const totalPixels = (+alculateArea.area) * (1000 / +resolution) ** 2;
const fileSizeBytes = totalPixels * bytesPerDataType[dataType];
const maxSize = fileSizeBytes / (+zip_level * 1024 ** 2);
if (maxSize > 100) {
message.info("预计文件超过100M,本网站是非盈利网站,超出网站的服务范围,请缩小面积")
return
}
const fileExt = cityInfo.path.split('/').pop();
const rasterFilename = fileExt!.split('.').shift() + "_" + newValue;
const newTime = `${rasterFilename}_${dayjs().format("YYYYMMDDHHmmss")}`
const outputRasterPath = `${PUBLIC_PATH}/${newTime}.tif`

const params = {
vectorDataFilePath,
outputRasterPath,
inputRasterPath: resPath
};
const result = await fetch(getEnv("/clip_raster"), setFetchHeader(params))
const { outputPath } = await result.json()
if (outputPath) {
const last = outputPath.split('/').pop()
const link = document.createElement('a');
link.href = `https://www.landcover100.com/download_raster?filePath=${last}`;
link.download = ".tif";
link.target = '_blank';
link.click();
}
setLoading(false)
}


const titleRender = (nodeData: any) => {
const hasChildren = nodeData.children
return (
<div>{
hasChildren
? nodeData.title
: <Radio.Group onChange={onRadioChange} value={value}>
<Radio value={nodeData.key}>
<div onClick={() => {
setValue("")
}}>{nodeData.title}</div>
</Radio>
</Radio.Group>}
</div>
)
}

return (
<div className={styles.dataPanel}>
<Spin spinning={loading} tip="请稍等,正在处理...">
<div className={styles.dataSelect}>
<div style={{ marginBottom: 8 }}>数据选择:</div>
<Tree
showLine
showIcon
selectable={false}
checkable={false}
onSelect={onSelect}
defaultExpandedKeys={['TDFG', "DEM"]}
checkStrictly={false}
treeData={mockTree}
titleRender={titleRender}
/>
</div>
</Spin>
<div style={{ marginTop: 16 }}>
<div style={{ marginBottom: 8 }}>下载数据:</div>
<Button type="primary" style={{ width: 250 }} onClick={() => onDownLoadData()}>数据下载</Button>
</div>
</div>
)
}
Loading