What this Skill does
Helps integrator developers correctly write integration code that calls the DJI Cloud API 2 Java SDK Map Domain modules:SafetyServiceModule and ElementModule。
Both share the same traits: they areattached to the Workspace, and areprimarily pure HTTP business interfaces(each with a small number of WebSocket subscriptions: SafetyService 1, Element 3), completely different from flight control/telemetry (Drone/Dock level, event-driven), and it is easy to make mistakes on "where should this be attached, do I need to seize control, and how do changes take effect".
Core value: The biggest pitfall in the map domain is not the API signature, butstate synchronization semantics — flight areas and offline maps modify the cloud-side definition,
and must then be sync delivered to the dock to take effect, and sync's returndoes not mean synchronization is complete, you also need to poll *SyncStatus。
When to trigger
- The user wants to write code to manage custom flight areas (
nfzgeofence /dfenceoperation area /nolandno-landing zone) - The user wants to deliver flight areas/offline maps to the dock or ground station (sync / query sync status)
- The user wants to generate, cancel, or toggle terrain obstacle offline maps
- The user wants to integrate Flysafe unlocking certificates (upload / verify / submit / query / toggle / clear)
- The user wants to query signal maps (4G / SDR coverage grids)
- The user wants to manage map elements (points/lines/polygons/circles, group hierarchy, visibility)
- The user encounters related errors: sync failure,
reasoncodes, geometry validation failure, incorrect callback parameters, module returns null
Do not trigger:
- The user wants tocall MCP Tools directly at runtime(
fh_flysafe/fh_unlock_license/fh_offline_mapand other operations/maintenance operations) →cloudapi2-mcp-guide - General SDK rules (initialization, auth headers, callback order, listen/cancel, seizing control), flight control/wayline tasks/media →
cloudapi2-sdk-core - Device lifecycle, firmware upgrade, HMS alerts →
cloudapi2-device-management - SEI/OSD telemetry, video streaming and map visualization fusion →
cloudapi2-sei-osd
Module location (first confirm where it is attached)
// ✅ 地图域模块在 Workspace 上
SafetyServiceModule safety = workspace.getSafetyServiceModule();
ElementModule element = workspace.getElementModule();
// ❌ 不在 drone / dock 上
// drone.getSafetyServiceModule() —— 不存在
// dock.getElementModule() —— 不存在
Either getter may return null(device/package not supported); you must null-check before calling.
| Module | Method count (verified from source) | Capabilities | Detailed documentation |
|---|---|---|---|
SafetyServiceModule |
22 | Signal maps, custom flight areas, terrain obstacle offline maps, Flysafe unlocking certificates | references/safety-service.md |
ElementModule |
11 | Map elements (points/lines/polygons/circles) CRUD + visibility + group hierarchy + 3 WS subscriptions | references/element.md |
Core rules
Rule 1: Callback order is uniformly (error, result), but a batch of methods only have (error)
In this domainthere is no getDeviceList that kind of data-first exception, but this does not mean all callbacks have two parameters:
| Form | Methods in the safety service |
|---|---|
onResult(error, result) |
Query class + Create/Update/Sync class |
onResult(error) error only |
deleteCustomizedFlightAreas、updateOfflineMapsModelInfo、cancelOfflineMapsModelInfo、setOfflineMaps、switchUnlockLicense、submitUnlockLicense、clearDeviceUnlockLicense |
onResult(message) no error |
listenDroneOfflineMapEnable(subscription push) |
Do not infer the number of parameters from "query/command"—in the command class, createCustomizedFlightAreas has result,deleteCustomizedFlightAreas does not.
If you write it wrong, the compiler will report an error, but when using lambdas it is easy to mistake error for result, so be sure to check the method table in references/ .
Rule 2: Cloud-side definition ≠ device takes effect,sync is a required step
创建/更新/删除飞行区 → 只改了云端
↓
syncCustomizedFlightAreas(网关 SN 列表) → 提交下发
↓
getFlightAreasSyncStatus(网关 SN 列表) → 轮询直到 synchronized / fail
syncXxxreturnscodeis only thesubmission result, and does not mean synchronization is complete- The sync object can only be agateway-type device SN(airport 1/2/3, ground station); an aircraft SN will not take effect
- Offline maps are isomorphic:
syncOfflineMaps→getOfflineMapsSyncStatus - On failure, give a readable reason according to the
DeviceSyncStatusResults.reasoncode (0–13 / 255, see reference)
Rule 3: Offline map generation is a long task
updateOfflineMapsModelInfo triggersmodel generation, not instant completion. You must poll getOfflineMapsModelInfo
until percent == 100 or status reaches a terminal state, then consider syncOfflineMaps. In the meantime, you can use cancelOfflineMapsModelInfo to cancel.
Rule 4: Unlock certificates are three-stage and steps cannot be skipped
uploadUnlockLicenseFile(本地路径) → 得到 fileStoreDir,拼出 fileKey
↓
checkUnlockLicense(fileKey, sn) → errFileNames / errCertMismatch 非空即中止
↓
submitUnlockLicense(sn, certUrl) → 提交解锁(仅 error)
check passing ≠ submit succeeding;submit before, you must confirm that check has no error items.
Rule 5: Geometry data formats are strict
ContentsGeometryData constraints are hard-coded in server-side validation:
| Type | Required | Constraint |
|---|---|---|
Circle |
center + radius |
radius > 0, unit meters |
Polygon |
coordinates |
≥3 points, vertex count < 255,first and last must be closed |
Polyline |
coordinates |
Only available for no-fly zones (noland) |
Coordinate format is always [经度, 纬度](longitude first). Maximum number of flight areas:nfz ≤ 100,noland ≤ 50。
Rule 6: Subscriptions must be paired with cancel; the two geometry model sets are not interchangeable
listenDroneOfflineMapEnable returns int listenId,< 0 indicates failure;@PreDestroy must call
cancelListenDroneOfflineMapEnable(listenId)。ElementModule 's listenElementCreate/Update/Delete
each also returns a listenId, which must be saved separately and paired with cancelListenElementXxx. (All other methods are one-time callbacks and do not need cancel.)
⚠️
SafetyServiceModuleflight area geometry usesContentsGeometryData.type = Circle/Polygon/Polyline;ElementModulegeometry usesElementGeometry.type = Point/LineString/Polygon, circles reusePoint + radius, has noCircleenum value. The two model sets have similar field names but different values; do not copy code across modules.
Decision tree routing
用户问题
├─ 飞行区(限飞区/作业区/禁降区)创建/查询/更新/删除
│ → references/safety-service.md#2-自定义飞行区
├─ 飞行区同步到设备 / 同步失败 / reason 码
│ → references/safety-service.md#25-同步到设备
├─ 离线地图生成/开关/进度/cancel/同步
│ → references/safety-service.md#3-离线地图
├─ 解锁证书上传/校验/提交/查询/开关/清除
│ → references/safety-service.md#4-解锁证书飞行解禁
├─ 信号地图 / H3 网格
│ → references/safety-service.md#1-信号地图
├─ 地图元素(点/线/面/圆)增删改查 / 分组层级 / WS 订阅
│ → references/element.md
├─ 模块获取为 null / 不知道挂在哪
│ → 本文件「模块位置」
├─ 回调参数写错、编译报错
│ → 本文件「规则 1」
└─ 要调 MCP 工具做运维(不写代码)
→ 转交 cloudapi2-mcp-guide
Workflow
This Skill follows the 7-chapter SOP defined by skills/_shared/sop-template.md and automatically selects the level according to L1/L2/L3.
Ch2 Clarifying questions
Points that must be clarified for map-domain requirements (missing one may lead to writing it wrong):
- Which type of object is the target: custom flight area / offline map / unlock certificate / map element / signal map
- Whether it needs to be delivered to the device: only cloud-side management (CRUD) or needs to take effect on the airport/aircraft (must sync)
- The target device type: aircraft SN or gateway device SN (sync only accepts the latter)
- Geometry type: circle / polygon / polyline (determines required fields and validation rules)
- Is there an existing object storage upload path: both offline map model files and unlock certificate files need to be uploaded first
Ch5 Coding Constraints
See constraints.yaml, all critical rules must be satisfied when generating code; general SDK rules (initialization, authentication, null checks)
refer to _shared/constraints-common.yamland are not repeated in this document.
Ch6 Verification Extensions
In addition to general verification, code in this domain must check:
- module getter has been null-checked (
getSafetyServiceModule()/getElementModule()) - callback parameter count matches the
references/method table (especially the batch with only error, and Element's 3 WS single-parameter callbacks) - whether the corresponding
syncis called after a modifying operation, and whether*SyncStatus - geometry data satisfies type constraints (closure, radius, type-region matching; Element circle reuses
Pointrather thanCircle) - all
listenXxxlistenIds are saved and paired with cancel (listenDroneOfflineMapEnable+ Element's create/update/delete three groups) - unlock flow includes the
checkbranch that aborts on failure
Anti-hallucination Rules (Mandatory)
- Example code must come from this Skill's references/ or user-provided documents, API signatures, field names, and enum values must not be generated from model memory
- Fabricating from memory is prohibited—bean field names in this domain have subtle differences (e.g.
deviceSnvssn、licenseIdhas both String and int), when uncertain, explicitly tell the user "needs confirmation from SDK documentation" - References must be traceable—any suggestion must be traceable to a
references/documentation path or official SDK source code
Authoritative References
| Document | Content |
|---|---|
references/safety-service.md |
SafetyServiceModule all 22 methods, bean fields, geometry constraints, sync semantics, reason codes |
references/element.md |
ElementModule all 11 methods, element/geometry models, grouping hierarchy, WS subscription triples |
skills/_shared/constraints-common.yaml |
Cross-Skill general constraints (callback/initialization/null checks) |
Shared Specifications
Cross-Skill shared constraints see
skills/_shared/constraints-common.yaml. Cross-Skill routing rules seeskills/_shared/skill-routing.md. When user intent is found to belong to another Skill's responsibility, switch according to the handover protocol. Output format seeskills/_shared/output-format.md. References are lazily loaded on demand; do not read all references/ at once.