Skip to content

Instantly share code, notes, and snippets.

@chenxuan520
Created November 22, 2025 12:57
Show Gist options
  • Select an option

  • Save chenxuan520/1790ddf0bdce0fee1b8a2e8f504004c9 to your computer and use it in GitHub Desktop.

Select an option

Save chenxuan520/1790ddf0bdce0fee1b8a2e8f504004c9 to your computer and use it in GitHub Desktop.
/**
* Cloudflare Worker - Tianditu (MapWorld) V2 Proxy
* 基于用户提供的 cURL 复刻,使用官方 Web 端 Key
* 坐标系:CGCS2000 (直接作为 WGS84 使用,无需转换,精度完美)
*/
// Nginx 伪装
const NGINX_HTML = `<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style>body{width:35em;margin:0 auto;font-family:Tahoma,Verdana,Arial,sans-serif;}</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed and working.</p><p><em>Thank you for using nginx.</em></p></body></html>`;
// 你的 cURL 中使用的关键 Token
const TIANDITU_TK = '75f0434f240669f4a2df6359275146d2';
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// 1. CORS 配置
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, User-Agent',
};
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
// 2. 首页伪装
if (url.pathname === '/' || url.pathname === '/index.html') {
return new Response(NGINX_HTML, {
headers: { 'Content-Type': 'text/html; charset=UTF-8', 'Server': 'nginx/1.18.0', ...corsHeaders },
});
}
// 3. 搜索接口
if (url.pathname === '/search') {
const query = url.searchParams.get('q');
if (!query) return new Response('[]', { headers: { 'Content-Type': 'application/json', ...corsHeaders } });
// ============================================================
// 构造天地图请求 (严格参考你的 cURL)
// ============================================================
// 构造 postStr JSON
const postData = {
"keyWord": query,
"level": "11", // 搜索等级,11-12 适合大部分 POI
"mapBound": "-180,-90,180,90", // 范围:全球 (覆盖你的 cURL 中的范围)
"queryType": "1", // 1: 普通关键字搜索 (最通用)
"count": "10", // 返回数量
"start": "0",
"yingjiType": 1, // 你的参数
"sourceType": 0, // 你的参数
"queryTerminal": 10000 // 你的参数
};
// 拼接目标 URL
// 注意:这里使用了你的 cURL 中的 v2 接口地址
const targetUrl = `https://api.tianditu.gov.cn/v2/search?type=query&postStr=${encodeURIComponent(JSON.stringify(postData))}&tk=${TIANDITU_TK}`;
try {
const response = await fetch(targetUrl, {
method: 'GET', // 天地图 V2 是 GET 请求
headers: {
// 严格复刻你的 Headers
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Origin': 'https://map.tianditu.gov.cn',
'Referer': 'https://map.tianditu.gov.cn/',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-site'
}
});
const data = await response.json();
// ============================================================
// 数据清洗 (转换为 Nominatim 格式)
// ============================================================
const osmResults = [];
// 情况1: 返回 POI 列表 (pois)
if (data.pois && Array.isArray(data.pois)) {
data.pois.forEach((item, index) => {
// 天地图 lonlat 格式通常是 "116.32,39.99" (逗号或空格分隔)
const coords = item.lonlat.split(/[\s,]+/);
if (coords.length >= 2) {
const lng = coords[0];
const lat = coords[1];
osmResults.push({
place_id: (item.userid ? parseInt(item.userid) : Date.now()) + index,
licence: "Data © Tianditu",
osm_type: "node",
osm_id: (item.phone ? parseInt(item.phone.replace(/\D/g,'').slice(0,8)) : Date.now()) + index,
// 构造包围盒
boundingbox: [
(parseFloat(lat) - 0.001).toString(),
(parseFloat(lat) + 0.001).toString(),
(parseFloat(lng) - 0.001).toString(),
(parseFloat(lng) + 0.001).toString()
],
lat: lat, // 原生坐标,无偏差
lon: lng, // 原生坐标,无偏差
display_name: `${item.name}, ${item.address || ''}`,
class: "place",
type: item.hotPointID ? "poi" : "unknown",
importance: 0.8 - (index * 0.05),
phone: item.phone || ""
});
}
});
}
// 情况2: 返回行政区划 (area)
else if (data.area && data.area.lonlat) {
const coords = data.area.lonlat.split(/[\s,]+/);
if (coords.length >= 2) {
osmResults.push({
place_id: Date.now(),
licence: "Data © Tianditu",
osm_type: "relation",
osm_id: Date.now(),
boundingbox: null, // 行政区 boundingbox 比较复杂,暂空
lat: coords[1],
lon: coords[0],
display_name: data.area.name,
class: "boundary",
type: "administrative",
importance: 0.95
});
}
}
// 情况3: 统计信息 (statistics) - 有时搜索省份会返回这个
else if (data.statistics && data.statistics.priorityCitys) {
data.statistics.priorityCitys.forEach((item, idx) => {
const coords = item.lonlat.split(/[\s,]+/);
if (coords.length >= 2) {
osmResults.push({
place_id: Date.now() + idx,
osm_type: "node",
osm_id: Date.now() + idx,
lat: coords[1],
lon: coords[0],
display_name: item.name,
class: "place",
type: "city",
importance: 0.9
});
}
});
}
return new Response(JSON.stringify(osmResults), {
headers: { 'Content-Type': 'application/json; charset=utf-8', ...corsHeaders }
});
} catch (err) {
// 容错返回空
return new Response(JSON.stringify([]), { headers: { 'Content-Type': 'application/json', ...corsHeaders } });
}
}
return new Response('Not Found', { status: 404 });
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment