---
title: "Tianditu Web Services"
description: "createTianditu wraps Tianditu place search, geocoding, administrative divisions and route planning behind one client; bind tk once, all results in WGS84."
canonical_url: "https://mapbox.mhaibaraai.cn/en/docs/utils/tianditu"
---
# Tianditu Web Services

> createTianditu wraps Tianditu place search, geocoding, administrative divisions and route planning behind one client; bind tk once, all results in WGS84.

## Introduction

`createTianditu` collapses several Tianditu Web services into a single client: `tk` is injected once at creation, so no method needs to pass it again. Tianditu uses the WGS84 (CGCS2000) datum; the client performs no coordinate conversion, and inputs/outputs pass through unchanged.

```ts
import { createTianditu } from '@movk/mapbox/utils/tianditu-client'

const td = createTianditu({ tk: tiandituApiToken })

await td.locate('The Bund, Shanghai') // place name → best-match point (auto-narrow + exact-match priority)
await td.search({ type: 'nearby', keyword: 'park', center: [116.48, 39.93], radius: 5000 })
await td.geocode('28 Lianhuachi West Rd, Haidian, Beijing') // address → precise coordinate
await td.reverseGeocode([116.37304, 39.92594]) // coordinate → structured address
await td.administrative('Beijing', { childLevel: 1 })
await td.route([116.35506, 39.92277], [116.39751, 39.90854], { mode: 'fastest' })
```

> [!WARNING]
> 
> Tianditu Web services must only be called on the server
> 
> , never import them in browser code. The search / geocoding / administrative / route APIs all require a 
> 
> server-type
> 
>  key (validated by the caller's IP allowlist), which is different from the 
> 
> browser-type
> 
>  key used by 
> 
> MapboxTiandituLayer
> 
> /
> 
> tiandituToken
> 
>  (validated by Referer)—they are not interchangeable. Calling these APIs with a browser-type key returns 
> 
> {"code":301012,"msg":"权限类型错误"}
> 
> . 
> 
> tk
> 
>  must be read from a private config source (e.g. Nuxt's 
> 
> runtimeConfig
> 
> , not 
> 
> runtimeConfig.public
> 
> ) and passed in explicitly.

A typical Nuxt usage wraps it in a `server/api` route:

```ts [server/api/geocode.get.ts]
import { createTianditu } from '@movk/mapbox/utils/tianditu-client'

export default defineEventHandler(async (event) => {
  const { keyword } = getQuery(event)
  const tk = useRuntimeConfig().tiandituApiToken // private field, not public
  return await createTianditu({ tk }).locate(String(keyword))
})
```

A plain Vue + Vite project has no Nitro; add a dev-time middleware in `vite.config.ts` via `configureServer` for the same effect:

```ts [vite.config.ts]
import { defineConfig, loadEnv } from 'vite'
import { createTianditu } from '@movk/mapbox/utils/tianditu-client'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '') // pass '' as the 3rd arg to read vars without the VITE_ prefix

  return {
    plugins: [
      {
        name: 'tianditu-geocode',
        configureServer(server) {
          server.middlewares.use('/api/geocode', async (req, res) => {
            const { keyword } = Object.fromEntries(new URL(req.url!, 'http://localhost').searchParams)
            const result = await createTianditu({ tk: env.TIANDITU_API_TOKEN }).locate(String(keyword))
            res.setHeader('Content-Type', 'application/json')
            res.end(JSON.stringify(result))
          })
        }
      }
    ]
  }
})
```

> [!NOTE]
> 
> Read 
> 
> tk
> 
>  only from an env var 
> 
> without
> 
>  the 
> 
> VITE_
> 
>  prefix (e.g. 
> 
> TIANDITU_API_TOKEN
> 
> )—Vite inlines 
> 
> VITE_
> 
> -prefixed vars verbatim into the browser bundle, which would leak a server-only key. Production also needs a real Node server (this middleware only runs under 
> 
> vite dev
> 
> ; a 
> 
> vite build
> 
>  output is static and doesn't carry this server-side logic).

> [!NOTE]
> See: /docs/extensions/tianditu
> 
> For the Tianditu basemap/annotation component, see 
> 
> MapboxTiandituLayer
> 
> ; this page covers pure server-side Web service capabilities and doesn't involve any map component.

## API

### `createTianditu()`

Create the client, binding `tk` and returning the methods below.

**options.tk** (`string`) *required*: Tianditu Web service token (server-type key).

### `search()`

Place search: one entry covering all 7 Tianditu `queryType`s, discriminated on `type` for input and on `resultType` for the normalized output. All coordinate inputs are WGS84.

```ts
type SearchParams =
  | { type: 'normal', keyword: string, bounds: Bounds, level: number, specify?: string }
  | { type: 'inView', keyword: string, bounds: Bounds, level: number }
  | { type: 'nearby', keyword: string, center: [number, number], radius: number }
  | { type: 'polygon', keyword: string, polygon: [number, number][] }
  | { type: 'district', specify: string, keyword?: string }
  | { type: 'category', specify: string, bounds: Bounds, dataTypes: string }
  | { type: 'statistics', specify: string, keyword?: string }
// shared optional: start / count / dataTypes / show

type SearchResult =
  | { kind: 'poi', count: number, pois: Poi[], suggestedDistrict?: { name: string, code: string } }
  | { kind: 'categories', categories: { name: string, count: number, pois: Poi[] }[] }
  | { kind: 'statistics', statistics: Statistics }
  | { kind: 'area', area: Area }
  | { kind: 'suggestion', suggestion: Suggestion }
  | { kind: 'line', lines: LineResult[] }
  | { kind: 'empty' }
```

Consume by branching on `result.kind`; `empty` means no data (not an error). Abnormal status codes throw `TiandituError` (carrying the Tianditu `infocode`). The `suggestedDistrict` on a POI result is the administrative district Tianditu inferred from the keyword (`locate()` uses it to auto-narrow the second query).

> [!NOTE]
> 
> A 
> 
> category
> 
>  search with a 
> 
> single
> 
> dataTypes
> 
>  value yields 
> 
> kind: 'poi'
> 
> ; with 
> 
> multiple
> 
>  (comma-separated) values Tianditu groups results by category name, yielding 
> 
> kind: 'categories'
> 
>  where each group's 
> 
> name
> 
>  is the queried category and 
> 
> count
> 
> /
> 
> pois
> 
>  map to it independently.

### `locate()`

Precise place location: resolves a landmark/place name to its best-match point, more accurate than a bare `search({ type: 'normal' })`—when `bounds` is omitted, it auto-narrows the search using the district suggestion and prioritizes exact-name matches.

**keyword** (`string`) *required*: Place / landmark keyword, e.g. "The Bund, Shanghai".

**options.bounds** (`[number, number, number, number]`): Custom search range [minx, miny, maxx, maxy]; passing it disables auto-narrowing. Defaults to the whole country.

**options.level** (`number`): Query level 1-18.
@defaultValue 10

**options.count** (`number`): Maximum number of results.

Returns `Promise<SearchResult>` (same shape as `search()`, typically `kind: 'poi'`).

### `searchNearby()`

Thin wrapper over `search({ type: 'nearby' })` that returns the POI list directly.

**keyword** (`string`) *required*: Search keyword, e.g. "bank", "metro station".

**center** (`[number, number]`) *required*: Center point coordinate [lng, lat].

**options.radius** (`number`): Search radius in meters.
@defaultValue 5000

**options.count** (`number`): Maximum number of results.

Returns `Promise<Poi[]>`; each `Poi` has `name`/`address?`/`location` (`[lng, lat]`) and more.

### `geocode()`

Forward geocoding: resolve a structured address into a precise coordinate; returns `undefined` when there is no result or the confidence is low (`score < 60`). Best for "a complete address with a house number"; for bare landmarks/place names use `locate()`.

**address** (`string`) *required*: Structured address, e.g. "28 Lianhuachi West Rd, Haidian, Beijing".

Returns `Promise<GeocodePoint | undefined>`: `location` (`[lng, lat]`), `level?`, `score?`.

### `reverseGeocode()`

Reverse geocoding: resolve a coordinate into a structured address; returns `undefined` when no result.

**point** (`[number, number]`) *required*: Coordinate [lng, lat].

Returns `Promise<ReverseGeocodeResult | undefined>`: `formattedAddress` plus `province?`/`city?`/`county?`/`road?`/`poi?`.

### `administrative()`

Administrative divisions: query center, boundary and sub-divisions by name (or GB code). The Tianditu WKT `MULTIPOLYGON` (real provinces/cities often contain exclave rings) is parsed into a GeoJSON `MultiPolygon` and converted to WGS84, ready for `MapboxLayer`.

**keyword** (`string`) *required*: Division name or 9-digit GB code, e.g. "Beijing" or "156110000".

**options.childLevel** (`0 | 1 | 2 | 3`): Sub-division depth: 0 none, 1 one level down, 2 two, 3 three.
@defaultValue 0

**options.boundary** (`boolean`): Whether to return the boundary outline.
@defaultValue true

Returns `Promise<AdministrativeDivision[]>`: `name`/`code`/`level`/`center`, `boundary?` (GeoJSON `MultiPolygon`), `children?`.

### `route()`

Driving/walking route planning: returns a path polyline along the real road network with real distance/duration; throws `TiandituError` when no route is found.

**origin** (`[number, number]`) *required*: Origin coordinate [lng, lat].

**destination** (`[number, number]`) *required*: Destination coordinate [lng, lat].

**options.mode** (`'fastest' | 'shortest' | 'avoid-highway' | 'walking'`): Route type: fastest / shortest / avoid highway / walking.
@defaultValue 'fastest'

**options.waypoints** (`[number, number][]`): Waypoint coordinates.

Returns `Promise<RouteResult>`: `distanceKm`/`durationMinutes`, `path` (GeoJSON `LineString`), `summary?`, and `center?`/`scale?` (camera parameters that frame the whole route).

## Changelog

See commit history for [src/runtime/utils/tianditu-request.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-request.ts)、[src/runtime/utils/tianditu-client.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-client.ts)、[src/runtime/utils/tianditu-geocoder.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-geocoder.ts)、[src/runtime/utils/tianditu-administrative.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-administrative.ts)、[src/runtime/utils/tianditu-route.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-route.ts)、[src/runtime/utils/tianditu-search.ts](https://github.com/mhaibaraai/movk-mapbox/commits/main/src/runtime/utils/tianditu-search.ts).

---

- [Tianditu Web API](http://lbs.tianditu.gov.cn/server/guide.html)


## Sitemap

See the full [sitemap](https://mapbox.mhaibaraai.cn/sitemap.md) for all pages.
