feat(图表): 世界地图支持下钻到国家

This commit is contained in:
jianneng-fit2cloud
2026-03-02 11:44:45 +08:00
committed by jianneng-fit2cloud
parent 36a8cdb988
commit 3972799fd4
10 changed files with 531 additions and 142281 deletions

View File

@@ -1,6 +1,10 @@
package io.dataease.map.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.dataease.api.map.dto.GeometryNodeCreator;
import io.dataease.api.map.vo.AreaNode;
import io.dataease.api.map.vo.CustomGeoArea;
@@ -156,7 +160,12 @@ public class MapManage {
File geoFile = buildGeoFile(code);
try {
boolean isChina = StringUtils.startsWith(code, "156");
if (isChina) {
file.transferTo(geoFile);
} else {
addGeoJsonField(code, file, geoFile);
}
} catch (IOException e) {
LogUtil.error(e.getMessage());
DEException.throwException(e);
@@ -315,4 +324,67 @@ public class MapManage {
}
return true;
}
/**
* 将GeoJSON文件中的每个feature的properties添加adcode字段值为根据父级code生成的子级code并将修改后的GeoJSON写入指定文件。
* @param code 当前行政区划编码
* @param file 上传的GeoJSON文件
* @param geoFile 目标文件修改后的GeoJSON将写入此文件
* @throws IOException 如果读取或写入文件时发生错误
*/
private void addGeoJsonField(String code, MultipartFile file, File geoFile) throws IOException {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode geoJson = mapper.readTree(file.getInputStream());
ArrayNode features = (ArrayNode) geoJson.get("features");
if (features != null) {
for (JsonNode feature : features) {
ObjectNode featureObj = (ObjectNode) feature;
ObjectNode properties = (ObjectNode) featureObj.get("properties");
if (properties == null) {
properties = mapper.createObjectNode();
featureObj.set("properties", properties);
}
properties.put("adcode", setChildAdcode(code));
}
}
mapper.writeValue(geoFile, geoJson);
} catch (Exception e) {
LogUtil.error(e.getMessage());
DEException.throwException(e);
}
}
/**
* 根据父级行政区划编码生成子级编码。 规则: 1. 若code为3位直接在末尾加1并补0到9位 2. 否则去除末尾所有0最后一位数字加1补0到9位 3.
* 若全为0返回1并补0到原长度 4. 若最后一位已为9抛出“层级过长”异常。
*
* @param code 父级行政区划编码
* @return 子级行政区划编码
* @throws IllegalArgumentException 层级过长时抛出
*/
private String setChildAdcode(String code) {
// 3位时直接补1并补0到9位
if (code.length() == 3) {
return StringUtils.rightPad(code + "1", 9, '0');
}
// 去除末尾所有0
String noTrailingZeros = StringUtils.stripEnd(code, "0");
// 如果全是0返回"1"并补0到原长度
if (StringUtils.isBlank(noTrailingZeros)) {
return StringUtils.rightPad("1", code.length(), '0');
}
if (noTrailingZeros.length() == 3) {
return StringUtils.rightPad(noTrailingZeros + "1", 9, '0');
}
// 最后一位数字加1
int lastDigit = noTrailingZeros.charAt(noTrailingZeros.length() - 1) - '0';
if (lastDigit == 9) {
throw new IllegalArgumentException("Hierarchy too deep");
}
String incremented = noTrailingZeros + (lastDigit + 1);
// 补0到9位
return StringUtils.rightPad(incremented, 9, '0');
}
}

View File

@@ -78,8 +78,14 @@ const getAreaMapping = async areaId => {
}, {})
}
const geoJson = await getGeoJsonFile(areaId)
const names = Object.keys(geoJson?.features[0]?.properties).filter(key => key.startsWith('NAME_'))
const nameKey = names[names.length - 1]
return geoJson.features.reduce((p, n) => {
p[n.properties.name] = n.properties.name
if (n.properties.name) {
p[n.properties.name] = n.properties.name
} else {
p[n.properties[nameKey]] = n.properties[nameKey]
}
return p
}, {})
}

View File

@@ -81,7 +81,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
async drawChart(drawOption: L7PlotDrawOptions<Choropleth>): Promise<Choropleth> {
const { chart, level, container, action, scope } = drawOption
const { areaId } = drawOption
const { areaId, gadmName } = drawOption
if (!areaId) {
return
}
@@ -133,6 +133,30 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
geoJson = cloneDeep(await getGeoJsonFile(areaId))
}
}
if (areaId.startsWith('geo_') && geoJson?.features?.length) {
const levelNames = Object.keys(geoJson?.features[0]?.properties).filter(key =>
key.startsWith('NAME_')
)
const nameKey = levelNames[levelNames.length - 1]
geoJson.features.forEach(item => {
if (item.properties[nameKey]) {
item.properties['name'] = item.properties[nameKey]
}
})
if (areaId.length > 7) {
geoJson.features = geoJson.features.filter(f => {
const names = Object.keys(f.properties)
.filter(key => key.startsWith('NAME_'))
.map(key => f.properties[key])
.filter(Boolean)
.join('@')
if (isEmpty(names)) {
return true
}
return names.replace(/@[^@]*$/, '') === gadmName
})
}
}
let data = []
// 自定义图例
if (!misc.mapAutoLegend && legend.show) {
@@ -225,17 +249,27 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
}
view.scene.map['keyboard'].disable()
view.on('fillAreaLayer:click', (ev: MapMouseEvent) => {
const data = ev.feature.properties
const evData = ev.feature.properties
if (areaId.startsWith('custom_')) {
data.name = data.areaName
data.adcode = '156'
evData.name = evData.areaName
evData.adcode = '156'
}
let adcode = evData.adcode
let names = ''
if (adcode + '' !== '156' && !areaId.startsWith('156')) {
adcode = 'geo_' + adcode
names = Object.keys(evData)
.filter(key => key.startsWith('NAME_'))
.map(key => evData[key])
.filter(Boolean)
.join('@')
}
action({
x: ev.x,
y: ev.y,
data: {
data,
extra: { adcode: data.adcode, scope: data.scope }
data: evData,
extra: { adcode, scope: evData.scope, gadmName: names }
}
})
})
@@ -314,7 +348,7 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
})
if (colorScale.length) {
options.color['value'] = colorScale.map(item =>
item.color ? new ColorWrapper(item.color) : new ColorWrapper(item)
item.color && item.value ? new ColorWrapper(item.color, item.value) : new ColorWrapper(item)
)
if (colorScale[0].value && !misc.mapAutoLegend) {
options.color['scale']['domain'] = [
@@ -334,10 +368,10 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
let value = '-'
if (item.value !== '') {
if (Array.isArray(item.value)) {
item.value.forEach((v, i) => {
item.value[i] = Number.isNaN(v) || v === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0)
})
value = item.value.join('-')
const arr = item.value.every(Number.isNaN) ? item.color.value : item.value
value = arr
.map(v => (Number.isNaN(v) || String(v) === 'NaN' ? 'NaN' : parseFloat(v).toFixed(0)))
.join('-')
} else {
const tmp = item.value as string
value = Number.isNaN(tmp) || tmp === 'NaN' ? 'NaN' : parseFloat(tmp).toFixed(0)
@@ -628,9 +662,13 @@ export class Map extends L7PlotChartView<ChoroplethOptions, Choropleth> {
class ColorWrapper {
private color: string
value?: any
constructor(color: string) {
constructor(color: string, value?: any) {
this.color = color
if (value !== undefined) {
this.value = value
}
}
toString(): string {

View File

@@ -1057,7 +1057,7 @@ export function configL7Tooltip(chart: Chart): TooltipOptions {
}
export function handleGeoJson(geoJson: FeatureCollection, nameMapping?: Record<string, string>) {
geoJson.features.forEach(item => {
geoJson?.features.forEach(item => {
if (!item.properties['centroid']) {
if (item.properties['center']) {
item.properties['centroid'] = item.properties['center']

View File

@@ -22,6 +22,8 @@ export interface L7PlotDrawOptions<P> extends AntVDrawOptions<P> {
level?: ViewLevel['level']
geoJson?: FeatureCollection
scope?: string[]
// 表示GADM层级关系name1@name2@name3,用于过滤geojson数据
gadmName?: string
}
// S2 or others to be defined next
export abstract class L7PlotChartView<

View File

@@ -499,7 +499,7 @@ export const getGeoJsonFile = async (areaId: string): Promise<FeatureCollection>
let geoJson = mapStore.mapCache[areaId]
if (!geoJson) {
const res = await getGeoJson(areaId)
geoJson = res.data
geoJson = res?.data
mapStore.setMap({ id: areaId, geoJson })
}
return toRaw(geoJson)

View File

@@ -277,15 +277,22 @@ const calcData = async (view, callback) => {
if (!res?.drillFilters?.length) {
dynamicAreaId.value = ''
scope = null
gadmName = null
} else {
const extra = view.chartExtRequest?.drill?.[res?.drillFilters?.length - 1].extra
const chartExtRequest = view.chartExtRequest || view.value?.chartExtRequest
const extra = chartExtRequest?.drill?.[res?.drillFilters?.length - 1].extra
dynamicAreaId.value = extra?.adcode + ''
scope = extra?.scope
gadmName = extra?.gadmName
// 地图
const map = parseJson(view.customAttr)?.map
if (map) {
let areaId = map.id
country.value = areaId.slice(0, 3)
// 世界下钻到国家,切换路径
if (country.value === '000' || dynamicAreaId.value?.startsWith('000')) {
country.value = chartExtRequest?.drill?.[0]?.extra?.adcode
}
}
if (!dynamicAreaId.value?.startsWith(country.value)) {
if (country.value === 'cus') {
@@ -373,6 +380,7 @@ const renderG2Plot = async (chart, chartView: G2PlotChartView<any, any>) => {
const dynamicAreaId = ref('')
const country = ref('')
let gadmName
const chartContainer = ref<HTMLElement>(null)
let scope
let mapTimer: number
@@ -401,7 +409,8 @@ const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any
chart,
areaId,
action,
scope
scope,
gadmName
})
callback?.()
emit('resetLoading')

View File

@@ -12,7 +12,7 @@ import type {
UploadProps
} from 'element-plus-secondary'
import request from '@/config/axios'
import { GeometryFrom } from './interface'
import { GeometryFrom, countryList } from './interface'
import { useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache()
const { t } = useI18n()
@@ -25,7 +25,8 @@ const state = reactive({
pid: null,
code: null,
name: null,
fileName: null
fileName: null,
country: null
}),
treeData: []
})
@@ -34,14 +35,49 @@ const treeProps = {
label: 'name',
disabled: 'readOnly'
}
const isCountry = computed(() => state.form.pid === '000')
const isChina = computed(() => state.form.pid?.startsWith('156'))
/**
* 获取父节点数量
* @param nodes
* @param pid
* @param count
*/
const getAncestorCount = (nodes, pid, count = 0) => {
for (const node of nodes) {
if (node.id === pid) {
return count
}
if (node.children) {
const res = getAncestorCount(node.children, pid, count + 1)
if (res !== null) return res
}
}
return null
}
const formatPid = computed(() => {
if (!state.form.pid) return ''
const pid = state.form.pid
return pid.replace(/(0+)$/g, '').replace(/\D/g, '')
if (!pid) return ''
// 156开头的pid没有父节点直接返回pid其他pid根据父先节点数量拼接字符串
const ancestorCount = pid.startsWith('156') ? 0 : getAncestorCount(state.treeData, pid) || ''
const clStr = ancestorCount ? ancestorCount : ''
// 先去掉非数字再用正则保留前3位和后面非0部分
const numericPid = pid.replace(/\D/g, '').replace(/(\d{3})(0+)$/, '$1') + clStr
return state.form.country ? numericPid.substring(3, numericPid.length) : numericPid
})
const codeTips = ref(t('system.at_the_end'))
const pidChange = () => {
state.form.code = null
state.form.country = null
}
/**
* 当pid变化时如果不是国家级别且不是中国则自动生成code
*/
const onPidChanged = () => {
if (!isCountry.value && !isChina.value) {
state.form.code = formatPid.value.padEnd(9, '0').slice(formatPid.value.length)
}
}
const validateCode = (_: any, value: any, callback: any) => {
const isCountry = !formatPid.value
@@ -72,6 +108,13 @@ const rule = reactive<FormRules>({
trigger: 'change'
}
],
country: [
{
required: true,
message: t('common.require'),
trigger: 'change'
}
],
code: [
{
required: true,
@@ -102,6 +145,7 @@ const edit = (pid?: string) => {
state.form.pid = pid
state.form.code = null
state.form.name = null
state.form.country = null
geoFile.value = null
state.form.fileName = null
dialogVisible.value = true
@@ -182,6 +226,13 @@ const buildFormData = (file, param) => {
formData.append('request', new Blob([JSON.stringify(param)], { type: 'application/json' }))
return formData
}
const countryChange = () => {
const countryItem = countryList.find(item => item.code === state.form.country) || null
state.form.code = countryItem.code
state.form.name = countryItem?.cn_name
}
defineExpose({
edit
})
@@ -214,9 +265,28 @@ defineExpose({
:render-after-expand="false"
:placeholder="t('common.please_select')"
@current-change="pidChange"
@change="onPidChanged"
/>
</el-form-item>
<el-form-item :label="t('system.country')" v-if="isCountry" prop="country">
<el-select
v-model="state.form.country"
:filterable="true"
:placeholder="t('common.please_select')"
@change="countryChange"
>
<el-option
v-for="item in countryList"
:key="item.name"
:label="item.cn_name"
:value="item.code"
>
<span style="float: left">{{ item.cn_name + ' ' + item.name }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('system.region_code')" prop="code">
<template v-slot:label>
<span class="area-code-label">
@@ -229,7 +299,7 @@ defineExpose({
</span>
</template>
<el-input v-if="state.form.pid" v-model="state.form.code">
<el-input v-if="state.form.pid" v-model="state.form.code" :disabled="isCountry || !isChina">
<template #prefix>
{{ formatPid }}
</template>
@@ -246,7 +316,7 @@ defineExpose({
</el-form-item>
<el-form-item :label="t('system.region_name')" prop="name">
<el-input v-model="state.form.name" />
<el-input v-model="state.form.name" :disabled="isCountry" />
</el-form-item>
<div class="geo-label-mask" />

View File

@@ -3,4 +3,314 @@ export interface GeometryFrom {
code?: string
name?: string
fileName?: string
country?: string
}
/**
* 国家信息接口
*/
export interface CountryInfo {
name: string
cn_name: string
code: string
iso_alpha3: string | null
}
/**
* 国家列表,包含英文名称、中文名称、数字代码和 ISO Alpha-3 代码
* 北塞浦路斯、锡亚琴冰川、克什米尔 不是 ISO 3166-1 标准中的主权国家,因此 iso_alpha3 设为 null。
* 英文名称采用了 ISO 3166-1 标准短名称。
*/
export const countryList: CountryInfo[] = [
{ name: 'Somalia', cn_name: '索马里', code: '706', iso_alpha3: 'SOM' },
{ name: 'Liechtenstein', cn_name: '列支敦士登', code: '438', iso_alpha3: 'LIE' },
{ name: 'Morocco', cn_name: '摩洛哥', code: '504', iso_alpha3: 'MAR' },
{ name: 'Western Sahara', cn_name: '西撒哈拉', code: '732', iso_alpha3: 'ESH' },
{ name: 'Serbia', cn_name: '塞尔维亚', code: '688', iso_alpha3: 'SRB' },
{ name: 'Afghanistan', cn_name: '阿富汗', code: '004', iso_alpha3: 'AFG' },
{ name: 'Angola', cn_name: '安哥拉', code: '024', iso_alpha3: 'AGO' },
{ name: 'Albania', cn_name: '阿尔巴尼亚', code: '008', iso_alpha3: 'ALB' },
{ name: 'Andorra', cn_name: '安道尔', code: '020', iso_alpha3: 'AND' },
{ name: 'United Arab Emirates', cn_name: '阿联酋', code: '784', iso_alpha3: 'ARE' },
{ name: 'Argentina', cn_name: '阿根廷', code: '032', iso_alpha3: 'ARG' },
{ name: 'Armenia', cn_name: '亚美尼亚', code: '051', iso_alpha3: 'ARM' },
{ name: 'American Samoa', cn_name: '美属萨摩亚', code: '016', iso_alpha3: 'ASM' },
{ name: 'French Southern Territories', cn_name: '法属南部领地', code: '260', iso_alpha3: 'ATF' },
{ name: 'Antigua and Barbuda', cn_name: '安提瓜和巴布达', code: '028', iso_alpha3: 'ATG' },
{ name: 'Australia', cn_name: '澳大利亚', code: '036', iso_alpha3: 'AUS' },
{ name: 'Austria', cn_name: '奥地利', code: '040', iso_alpha3: 'AUT' },
{ name: 'Azerbaijan', cn_name: '阿塞拜疆', code: '031', iso_alpha3: 'AZE' },
{ name: 'Burundi', cn_name: '布隆迪', code: '108', iso_alpha3: 'BDI' },
{ name: 'Belgium', cn_name: '比利时', code: '056', iso_alpha3: 'BEL' },
{ name: 'Benin', cn_name: '贝宁', code: '204', iso_alpha3: 'BEN' },
{ name: 'Burkina Faso', cn_name: '布基纳法索', code: '854', iso_alpha3: 'BFA' },
{ name: 'Bangladesh', cn_name: '孟加拉国', code: '050', iso_alpha3: 'BGD' },
{ name: 'Bulgaria', cn_name: '保加利亚', code: '100', iso_alpha3: 'BGR' },
{ name: 'Bahrain', cn_name: '巴林', code: '048', iso_alpha3: 'BHR' },
{ name: 'Bahamas', cn_name: '巴哈马', code: '044', iso_alpha3: 'BHS' },
{
name: 'Bosnia and Herzegovina',
cn_name: '波斯尼亚和黑塞哥维那',
code: '070',
iso_alpha3: 'BIH'
},
{ name: 'Belarus', cn_name: '白俄罗斯', code: '112', iso_alpha3: 'BLR' },
{ name: 'Belize', cn_name: '伯利兹', code: '084', iso_alpha3: 'BLZ' },
{ name: 'Bermuda', cn_name: '百慕大', code: '060', iso_alpha3: 'BMU' },
{ name: 'Bolivia', cn_name: '玻利维亚', code: '068', iso_alpha3: 'BOL' },
{ name: 'Brazil', cn_name: '巴西', code: '076', iso_alpha3: 'BRA' },
{ name: 'Barbados', cn_name: '巴巴多斯', code: '052', iso_alpha3: 'BRB' },
{ name: 'Brunei Darussalam', cn_name: '文莱', code: '096', iso_alpha3: 'BRN' },
{ name: 'Bhutan', cn_name: '不丹', code: '064', iso_alpha3: 'BTN' },
{ name: 'Botswana', cn_name: '博茨瓦纳', code: '072', iso_alpha3: 'BWA' },
{ name: 'Central African Republic', cn_name: '中非共和国', code: '140', iso_alpha3: 'CAF' },
{ name: 'Canada', cn_name: '加拿大', code: '124', iso_alpha3: 'CAN' },
{ name: 'Switzerland', cn_name: '瑞士', code: '756', iso_alpha3: 'CHE' },
{ name: 'Chile', cn_name: '智利', code: '152', iso_alpha3: 'CHL' },
{ name: 'China', cn_name: '中国', code: '156', iso_alpha3: 'CHN' },
{ name: "Côte d'Ivoire", cn_name: '科特迪瓦', code: '384', iso_alpha3: 'CIV' },
{ name: 'Cameroon', cn_name: '喀麦隆', code: '120', iso_alpha3: 'CMR' },
{
name: 'Democratic Republic of the Congo',
cn_name: '刚果',
code: '180',
iso_alpha3: 'COD'
},
{ name: 'Republic of the Congo', cn_name: '刚果', code: '178', iso_alpha3: 'COG' },
{ name: 'Colombia', cn_name: '哥伦比亚', code: '170', iso_alpha3: 'COL' },
{ name: 'Comoros', cn_name: '科摩罗', code: '174', iso_alpha3: 'COM' },
{ name: 'Cabo Verde', cn_name: '佛得角', code: '132', iso_alpha3: 'CPV' },
{ name: 'Costa Rica', cn_name: '哥斯达黎加', code: '188', iso_alpha3: 'CRI' },
{ name: 'Cuba', cn_name: '古巴', code: '192', iso_alpha3: 'CUB' },
{ name: 'Curaçao', cn_name: '库拉索', code: '531', iso_alpha3: 'CUW' },
{ name: 'Cayman Islands', cn_name: '开曼群岛', code: '136', iso_alpha3: 'CYM' },
// { name: 'Northern Cyprus', cn_name: '北塞浦路斯', code: '197', iso_alpha3: null }, // 非ISO标准无官方Alpha3
{ name: 'Cyprus', cn_name: '塞浦路斯', code: '196', iso_alpha3: 'CYP' },
{ name: 'Czechia', cn_name: '捷克', code: '205', iso_alpha3: 'CZE' }, // 旧码 203 (Czechoslovakia), 新码 205 对应 Czech Republic? 注意ISO中捷克是203 (CZE)。这里输入是205可能是数据源差异标准ISO 3166-1 numeric for Czechia is 203. 但为了匹配您的Key我保留205Alpha3填CZE。
// 修正:检查发现 ISO 3166-1 Numeric for Czech Republic is 203. 205 可能是旧数据或错误。但我会按您的 Key 输出Alpha3 依然是 CZE。
{ name: 'Germany', cn_name: '德国', code: '276', iso_alpha3: 'DEU' },
{ name: 'Djibouti', cn_name: '吉布提', code: '262', iso_alpha3: 'DJI' },
{ name: 'Dominica', cn_name: '多米尼克', code: '212', iso_alpha3: 'DMA' },
{ name: 'Denmark', cn_name: '丹麦', code: '208', iso_alpha3: 'DNK' },
{ name: 'Dominican Republic', cn_name: '多米尼加', code: '214', iso_alpha3: 'DOM' },
{ name: 'Algeria', cn_name: '阿尔及利亚', code: '012', iso_alpha3: 'DZA' },
{ name: 'Ecuador', cn_name: '厄瓜多尔', code: '218', iso_alpha3: 'ECU' },
{ name: 'Egypt', cn_name: '埃及', code: '818', iso_alpha3: 'EGY' },
{ name: 'Eritrea', cn_name: '厄立特里亚', code: '232', iso_alpha3: 'ERI' },
{ name: 'Spain', cn_name: '西班牙', code: '724', iso_alpha3: 'ESP' },
{ name: 'Estonia', cn_name: '爱沙尼亚', code: '233', iso_alpha3: 'EST' },
{ name: 'Ethiopia', cn_name: '埃塞俄比亚', code: '231', iso_alpha3: 'ETH' },
{ name: 'Finland', cn_name: '芬兰', code: '246', iso_alpha3: 'FIN' },
{ name: 'Fiji', cn_name: '斐济', code: '242', iso_alpha3: 'FJI' },
{ name: 'Falkland Islands (Malvinas)', cn_name: '福克兰群岛', code: '238', iso_alpha3: 'FLK' },
{ name: 'French Guiana', cn_name: '法属圭亚那', code: '254', iso_alpha3: 'GUF' },
{ name: 'France', cn_name: '法国', code: '250', iso_alpha3: 'FRA' },
{ name: 'Faroe Islands', cn_name: '法罗群岛', code: '234', iso_alpha3: 'FRO' },
{
name: 'Micronesia, Federated States of',
cn_name: '密克罗尼西亚',
code: '583',
iso_alpha3: 'FSM'
},
{ name: 'Gabon', cn_name: '加蓬', code: '266', iso_alpha3: 'GAB' },
{
name: 'United Kingdom of Great Britain and Northern Ireland',
cn_name: '英国',
code: '826',
iso_alpha3: 'GBR'
},
{ name: 'Georgia', cn_name: '格鲁吉亚', code: '268', iso_alpha3: 'GEO' },
{ name: 'Ghana', cn_name: '加纳', code: '288', iso_alpha3: 'GHA' },
{ name: 'Guinea', cn_name: '几内亚', code: '324', iso_alpha3: 'GIN' },
{ name: 'Gambia', cn_name: '冈比亚', code: '270', iso_alpha3: 'GMB' },
{ name: 'Guinea-Bissau', cn_name: '几内亚比绍', code: '624', iso_alpha3: 'GNB' },
{ name: 'Equatorial Guinea', cn_name: '赤道几内亚', code: '226', iso_alpha3: 'GNQ' },
{ name: 'Greece', cn_name: '希腊', code: '300', iso_alpha3: 'GRC' },
{ name: 'Grenada', cn_name: '格林纳达', code: '308', iso_alpha3: 'GRD' },
{ name: 'Greenland', cn_name: '格陵兰', code: '304', iso_alpha3: 'GRL' },
{ name: 'Guatemala', cn_name: '危地马拉', code: '320', iso_alpha3: 'GTM' },
{ name: 'Guam', cn_name: '关岛', code: '316', iso_alpha3: 'GUM' },
{ name: 'Guyana', cn_name: '圭亚那', code: '328', iso_alpha3: 'GUY' },
{
name: 'Heard Island and McDonald Islands',
cn_name: '赫德岛和麦克唐纳群岛',
code: '334',
iso_alpha3: 'HMD'
},
{ name: 'Honduras', cn_name: '洪都拉斯', code: '340', iso_alpha3: 'HND' },
{ name: 'Croatia', cn_name: '克罗地亚', code: '191', iso_alpha3: 'HRV' },
{ name: 'Haiti', cn_name: '海地', code: '332', iso_alpha3: 'HTI' },
{ name: 'Hungary', cn_name: '匈牙利', code: '348', iso_alpha3: 'HUN' },
{ name: 'Indonesia', cn_name: '印度尼西亚', code: '360', iso_alpha3: 'IDN' },
{ name: 'Isle of Man', cn_name: '马恩岛', code: '833', iso_alpha3: 'IMN' },
{ name: 'India', cn_name: '印度', code: '356', iso_alpha3: 'IND' },
{
name: 'British Indian Ocean Territory',
cn_name: '英属印度洋领地',
code: '086',
iso_alpha3: 'IOT'
},
{ name: 'Ireland', cn_name: '爱尔兰', code: '372', iso_alpha3: 'IRL' },
{ name: 'Iran, Islamic Republic of', cn_name: '伊朗', code: '364', iso_alpha3: 'IRN' },
{ name: 'Iraq', cn_name: '伊拉克', code: '368', iso_alpha3: 'IRQ' },
{ name: 'Iceland', cn_name: '冰岛', code: '352', iso_alpha3: 'ISL' },
{ name: 'Israel', cn_name: '以色列', code: '376', iso_alpha3: 'ISR' },
{ name: 'Italy', cn_name: '意大利', code: '380', iso_alpha3: 'ITA' },
{ name: 'Jamaica', cn_name: '牙买加', code: '388', iso_alpha3: 'JAM' },
{ name: 'Jordan', cn_name: '约旦', code: '400', iso_alpha3: 'JOR' },
{ name: 'Japan', cn_name: '日本', code: '392', iso_alpha3: 'JPN' },
// { name: 'Siachen Glacier', cn_name: '锡亚琴冰川', code: '111', iso_alpha3: null }, // 非国家,无代码
{ name: 'Kazakhstan', cn_name: '哈萨克斯坦', code: '398', iso_alpha3: 'KAZ' },
{ name: 'Kenya', cn_name: '肯尼亚', code: '404', iso_alpha3: 'KEN' },
{ name: 'Kyrgyzstan', cn_name: '吉尔吉斯斯坦', code: '417', iso_alpha3: 'KGZ' },
{ name: 'Cambodia', cn_name: '柬埔寨', code: '116', iso_alpha3: 'KHM' },
{ name: 'Kiribati', cn_name: '基里巴斯', code: '296', iso_alpha3: 'KIR' },
{ name: 'Republic of Korea', cn_name: '韩国', code: '410', iso_alpha3: 'KOR' },
{ name: 'Kuwait', cn_name: '科威特', code: '414', iso_alpha3: 'KWT' },
{ name: "Lao People's Democratic Republic", cn_name: '老挝', code: '418', iso_alpha3: 'LAO' },
{ name: 'Lebanon', cn_name: '黎巴嫩', code: '422', iso_alpha3: 'LBN' },
{ name: 'Liberia', cn_name: '利比里亚', code: '430', iso_alpha3: 'LBR' },
{ name: 'Libya', cn_name: '利比亚', code: '434', iso_alpha3: 'LBY' },
{ name: 'Saint Lucia', cn_name: '圣卢西亚', code: '662', iso_alpha3: 'LCA' },
{ name: 'Sri Lanka', cn_name: '斯里兰卡', code: '144', iso_alpha3: 'LKA' },
{ name: 'Lesotho', cn_name: '莱索托', code: '426', iso_alpha3: 'LSO' },
{ name: 'Lithuania', cn_name: '立陶宛', code: '440', iso_alpha3: 'LTU' },
{ name: 'Luxembourg', cn_name: '卢森堡', code: '442', iso_alpha3: 'LUX' },
{ name: 'Latvia', cn_name: '拉脱维亚', code: '428', iso_alpha3: 'LVA' },
{ name: 'Moldova, Republic of', cn_name: '摩尔多瓦', code: '498', iso_alpha3: 'MDA' },
{ name: 'Madagascar', cn_name: '马达加斯加', code: '450', iso_alpha3: 'MDG' },
{ name: 'Mexico', cn_name: '墨西哥', code: '484', iso_alpha3: 'MEX' },
{ name: 'North Macedonia', cn_name: '北马其顿', code: '807', iso_alpha3: 'MKD' },
{ name: 'Mali', cn_name: '马里', code: '466', iso_alpha3: 'MLI' },
{ name: 'Malta', cn_name: '马耳他', code: '470', iso_alpha3: 'MLT' },
{ name: 'Myanmar', cn_name: '缅甸', code: '104', iso_alpha3: 'MMR' },
{ name: 'Montenegro', cn_name: '黑山', code: '499', iso_alpha3: 'MNE' },
{ name: 'Mongolia', cn_name: '蒙古', code: '496', iso_alpha3: 'MNG' },
{ name: 'Northern Mariana Islands', cn_name: '北马里亚纳群岛', code: '580', iso_alpha3: 'MNP' },
{ name: 'Mozambique', cn_name: '莫桑比克', code: '508', iso_alpha3: 'MOZ' },
{ name: 'Mauritania', cn_name: '毛里塔尼亚', code: '478', iso_alpha3: 'MRT' },
{ name: 'Montserrat', cn_name: '蒙特塞拉特', code: '500', iso_alpha3: 'MSR' },
{ name: 'Mauritius', cn_name: '毛里求斯', code: '480', iso_alpha3: 'MUS' },
{ name: 'Malawi', cn_name: '马拉维', code: '454', iso_alpha3: 'MWI' },
{ name: 'Malaysia', cn_name: '马来西亚', code: '458', iso_alpha3: 'MYS' },
{ name: 'Namibia', cn_name: '纳米比亚', code: '516', iso_alpha3: 'NAM' },
{ name: 'New Caledonia', cn_name: '新喀里多尼亚', code: '540', iso_alpha3: 'NCL' },
{ name: 'Niger', cn_name: '尼日尔', code: '562', iso_alpha3: 'NER' },
{ name: 'Nigeria', cn_name: '尼日利亚', code: '566', iso_alpha3: 'NGA' },
{ name: 'Nicaragua', cn_name: '尼加拉瓜', code: '558', iso_alpha3: 'NIC' },
{ name: 'Niue', cn_name: '纽埃', code: '570', iso_alpha3: 'NIU' },
{ name: 'Netherlands', cn_name: '荷兰', code: '528', iso_alpha3: 'NLD' },
{ name: 'Norway', cn_name: '挪威', code: '578', iso_alpha3: 'NOR' },
{ name: 'Nepal', cn_name: '尼泊尔', code: '524', iso_alpha3: 'NPL' },
{ name: 'New Zealand', cn_name: '新西兰', code: '554', iso_alpha3: 'NZL' },
{ name: 'Oman', cn_name: '阿曼', code: '512', iso_alpha3: 'OMN' },
{ name: 'Pakistan', cn_name: '巴基斯坦', code: '586', iso_alpha3: 'PAK' },
{ name: 'Panama', cn_name: '巴拿马', code: '591', iso_alpha3: 'PAN' },
{ name: 'Peru', cn_name: '秘鲁', code: '604', iso_alpha3: 'PER' },
{ name: 'Philippines', cn_name: '菲律宾', code: '608', iso_alpha3: 'PHL' },
{ name: 'Palau', cn_name: '帕劳', code: '585', iso_alpha3: 'PLW' },
{ name: 'Papua New Guinea', cn_name: '巴布亚新几内亚', code: '598', iso_alpha3: 'PNG' },
{ name: 'Poland', cn_name: '波兰', code: '616', iso_alpha3: 'POL' },
{ name: 'Puerto Rico', cn_name: '波多黎各', code: '630', iso_alpha3: 'PRI' },
{
name: "Democratic People's Republic of Korea",
cn_name: '朝鲜',
code: '408',
iso_alpha3: 'PRK'
},
{ name: 'Portugal', cn_name: '葡萄牙', code: '620', iso_alpha3: 'PRT' },
{ name: 'Paraguay', cn_name: '巴拉圭', code: '600', iso_alpha3: 'PRY' },
{ name: 'State of Palestine', cn_name: '巴勒斯坦', code: '275', iso_alpha3: 'PSE' },
{ name: 'French Polynesia', cn_name: '法属波利尼西亚', code: '258', iso_alpha3: 'PYF' },
{ name: 'Qatar', cn_name: '卡塔尔', code: '634', iso_alpha3: 'QAT' },
{ name: 'Romania', cn_name: '罗马尼亚', code: '642', iso_alpha3: 'ROU' },
{ name: 'Russian Federation', cn_name: '俄罗斯', code: '643', iso_alpha3: 'RUS' },
{ name: 'Rwanda', cn_name: '卢旺达', code: '646', iso_alpha3: 'RWA' },
{ name: 'Saudi Arabia', cn_name: '沙特阿拉伯', code: '682', iso_alpha3: 'SAU' },
{ name: 'Sudan', cn_name: '苏丹', code: '729', iso_alpha3: 'SDN' },
{ name: 'South Sudan', cn_name: '南苏丹', code: '728', iso_alpha3: 'SSD' },
{ name: 'Senegal', cn_name: '塞内加尔', code: '686', iso_alpha3: 'SEN' },
{ name: 'Singapore', cn_name: '新加坡', code: '702', iso_alpha3: 'SGP' },
{
name: 'South Georgia and the South Sandwich Islands',
cn_name: '南乔治亚岛',
code: '239',
iso_alpha3: 'SGS'
},
{
name: 'Saint Helena, Ascension and Tristan da Cunha',
cn_name: '圣赫勒拿',
code: '654',
iso_alpha3: 'SHN'
},
{ name: 'Solomon Islands', cn_name: '所罗门群岛', code: '090', iso_alpha3: 'SLB' },
{ name: 'Sierra Leone', cn_name: '塞拉利昂', code: '694', iso_alpha3: 'SLE' },
{ name: 'El Salvador', cn_name: '萨尔瓦多', code: '222', iso_alpha3: 'SLV' },
{
name: 'Saint Pierre and Miquelon',
cn_name: '圣皮埃尔和密克隆',
code: '666',
iso_alpha3: 'SPM'
},
{ name: 'Sao Tome and Principe', cn_name: '圣多美和普林西比', code: '678', iso_alpha3: 'STP' },
{ name: 'Suriname', cn_name: '苏里南', code: '740', iso_alpha3: 'SUR' },
{ name: 'Slovakia', cn_name: '斯洛伐克', code: '703', iso_alpha3: 'SVK' },
{ name: 'Slovenia', cn_name: '斯洛文尼亚', code: '705', iso_alpha3: 'SVN' },
{ name: 'Sweden', cn_name: '瑞典', code: '752', iso_alpha3: 'SWE' },
{ name: 'Eswatini', cn_name: '斯威士兰', code: '748', iso_alpha3: 'SWZ' },
{ name: 'Seychelles', cn_name: '塞舌尔', code: '690', iso_alpha3: 'SYC' },
{ name: 'Syrian Arab Republic', cn_name: '叙利亚', code: '760', iso_alpha3: 'SYR' },
{
name: 'Turks and Caicos Islands',
cn_name: '特克斯和凯科斯群岛',
code: '796',
iso_alpha3: 'TCA'
},
{ name: 'Chad', cn_name: '乍得', code: '148', iso_alpha3: 'TCD' },
{ name: 'Togo', cn_name: '多哥', code: '768', iso_alpha3: 'TGO' },
{ name: 'Thailand', cn_name: '泰国', code: '764', iso_alpha3: 'THA' },
{ name: 'Tajikistan', cn_name: '塔吉克斯坦', code: '762', iso_alpha3: 'TJK' },
{ name: 'Turkmenistan', cn_name: '土库曼斯坦', code: '795', iso_alpha3: 'TKM' },
{ name: 'Timor-Leste', cn_name: '东帝汶', code: '626', iso_alpha3: 'TLS' },
{ name: 'Tonga', cn_name: '汤加', code: '776', iso_alpha3: 'TON' },
{ name: 'Trinidad and Tobago', cn_name: '特立尼达和多巴哥', code: '780', iso_alpha3: 'TTO' },
{ name: 'Tunisia', cn_name: '突尼斯', code: '788', iso_alpha3: 'TUN' },
{ name: 'Turkey', cn_name: '土耳其', code: '792', iso_alpha3: 'TUR' },
{ name: 'United Republic of Tanzania', cn_name: '坦桑尼亚', code: '834', iso_alpha3: 'TZA' },
{ name: 'Uganda', cn_name: '乌干达', code: '800', iso_alpha3: 'UGA' },
{ name: 'Ukraine', cn_name: '乌克兰', code: '804', iso_alpha3: 'UKR' },
{ name: 'Uruguay', cn_name: '乌拉圭', code: '858', iso_alpha3: 'URY' },
{ name: 'United States of America', cn_name: '美国', code: '840', iso_alpha3: 'USA' },
{ name: 'Uzbekistan', cn_name: '乌兹别克斯坦', code: '860', iso_alpha3: 'UZB' },
{
name: 'Saint Vincent and the Grenadines',
cn_name: '圣文森特和格林纳丁斯',
code: '670',
iso_alpha3: 'VCT'
},
{
name: 'Venezuela, Bolivarian Republic of',
cn_name: '委内瑞拉',
code: '862',
iso_alpha3: 'VEN'
},
{ name: 'Virgin Islands, U.S.', cn_name: '美属维尔京群岛', code: '850', iso_alpha3: 'VIR' },
{ name: 'Viet Nam', cn_name: '越南', code: '704', iso_alpha3: 'VNM' },
{ name: 'Vanuatu', cn_name: '瓦努阿图', code: '548', iso_alpha3: 'VUT' },
{ name: 'Samoa', cn_name: '萨摩亚', code: '882', iso_alpha3: 'WSM' },
{ name: 'Yemen', cn_name: '也门', code: '887', iso_alpha3: 'YEM' },
{ name: 'South Africa', cn_name: '南非', code: '710', iso_alpha3: 'ZAF' },
{ name: 'Zambia', cn_name: '赞比亚', code: '894', iso_alpha3: 'ZMB' },
{ name: 'Zimbabwe', cn_name: '津巴布韦', code: '716', iso_alpha3: 'ZWE' },
// { name: 'Kashmir', cn_name: '克什米尔', code: '113', iso_alpha3: null }, // 非国家,无代码
{ name: 'San Marino', cn_name: '圣马力诺', code: '674', iso_alpha3: 'SMR' },
{ name: 'Holy See (Vatican City State)', cn_name: '梵蒂冈', code: '336', iso_alpha3: 'VAT' },
{ name: 'Monaco', cn_name: '摩纳哥', code: '492', iso_alpha3: 'MCO' },
{ name: 'Maldives', cn_name: '马尔代夫', code: '462', iso_alpha3: 'MDV' },
{ name: 'Marshall Islands', cn_name: '马绍尔群岛', code: '584', iso_alpha3: 'MHL' },
{ name: 'Nauru', cn_name: '瑙鲁', code: '520', iso_alpha3: 'NRU' },
{ name: 'Tokelau', cn_name: '托克劳', code: '772', iso_alpha3: 'TKL' },
{ name: 'Cook Islands', cn_name: '库克群岛', code: '184', iso_alpha3: 'COK' },
{ name: 'Saint Kitts and Nevis', cn_name: '圣基茨和尼维斯', code: '659', iso_alpha3: 'KNA' },
{ name: 'Tuvalu', cn_name: '图瓦卢', code: '798', iso_alpha3: 'TUV' }
]

File diff suppressed because one or more lines are too long