mcpField
A
arcgis-editing

arcgis-editing

github.com/saschabrunnerch开发2347 次安装29 周前更新
代码仓库

关于 arcgis-editing

arcgis-editing 是面向 ArcGIS 开发者的编辑技能,涵盖 Editor、表单、要素修改和版本管理。它还提供附件、子类型及相关记录的操作参考。

主要功能

  1. 01配置 Editor 和图层编辑能力
  2. 02构建并验证要素表单
  3. 03使用 applyEdits 修改要素
  4. 04管理子类型与分支版本
  5. 05查询和处理附件及相关记录

适用场景

  1. 01为地图应用添加要素创建和编辑界面
  2. 02按图层配置属性、几何和附件编辑
  3. 03在分支版本中开展协作编辑
  4. 04以代码批量添加或更新要素

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/saschabrunnerch/arcgis-maps-sdk-js-ai-context --skill arcgis-editing

安装后在 Claude Code 中输入/arcgis-editing确认已加载。

附带文件AGENTS.md

详细介绍

ArcGIS 编辑

使用此技能编辑要素,包括配置 Editor、表单模板、子类型、附件、版本管理和程序化编辑。

导入模式

直接 ESM 导入
import Editor from "@arcgis/core/widgets/Editor.js";
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";
import FormTemplate from "@arcgis/core/form/FormTemplate.js";
动态导入(CDN)
const Editor = await $arcgis.import("@arcgis/core/widgets/Editor.js");
const FeatureForm = await $arcgis.import("@arcgis/core/widgets/FeatureForm.js");
const FormTemplate = await $arcgis.import("@arcgis/core/form/FormTemplate.js");

注意: 此技能中的示例使用直接 ESM 导入。若要使用 CDN,请将 import X from "path" 替换为 const X = await $arcgis.import("path")。

Editor 组件

添加编辑功能最简单的方式是使用 <arcgis-editor> 组件。

基本用法
<arcgis-map item-id="YOUR_EDITABLE_WEBMAP_ID">
  <arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>
主要属性
属性类型默认值说明
heading-levelnumber用于无障碍访问的标题级别
hide-create-features-sectionbooleanfalse隐藏创建要素面板
hide-edit-features-sectionbooleanfalse隐藏编辑要素面板
hide-labels-togglebooleanfalse隐藏标签切换开关
hide-merge-buttonbooleanfalse隐藏合并要素按钮
hide-settings-menubooleanfalse隐藏设置菜单
hide-sketchbooleanfalse隐藏草图工具
hide-split-buttonbooleanfalse隐藏拆分要素按钮
hide-tooltips-togglebooleanfalse隐藏工具提示切换开关
hide-undo-redo-buttonsbooleanfalse隐藏撤消/重做按钮
hide-zoom-to-buttonbooleanfalse隐藏缩放至按钮
labelstring组件标签
layer-infosLayerInfo[]图层编辑配置
snapping-optionsSnappingOptions捕捉配置
sync-view-selectionbooleanfalse将选择与视图同步
主要事件
事件说明
arcgisSketchCreate创建要素草图时触发
arcgisSketchUpdate更新要素草图时触发
arcgisPropertyChangeactiveWorkflow 或 state 更改时触发
arcgisReady组件就绪时触发
主要方法
方法说明
cancelWorkflow()取消当前编辑工作流
deleteFeatureFromWorkflow()删除正在编辑的要素
startCreateFeaturesWorkflowAtFeatureTypeSelection()开始创建要素
startUpdateWorkflowAtFeatureEdit(feature)开始编辑指定要素
startUpdateWorkflowAtFeatureSelection()开始选择要更新的要素
配置 Editor 组件
<arcgis-map basemap="streets-navigation-vector">
  <arcgis-editor slot="top-right"></arcgis-editor>
</arcgis-map>

<script type="module">
  const FeatureLayer = await $arcgis.import(
    "@arcgis/core/layers/FeatureLayer.js",
  );

  const mapElement = document.querySelector("arcgis-map");
  const view = await mapElement.view;
  await view.when();

  const layer = new FeatureLayer({
    url: "https://services.arcgis.com/.../FeatureServer/0",
  });
  mapElement.map.add(layer);

  const editor = document.querySelector("arcgis-editor");
  editor.layerInfos = [
    {
      layer: layer,
      formTemplate: {
        title: "Edit Feature",
        elements: [
          { type: "field", fieldName: "name", label: "Name" },
          { type: "field", fieldName: "category", label: "Category" },
        ],
      },
      addEnabled: true,
      updateEnabled: true,
      deleteEnabled: false,
    },
  ];
</script>

Editor 微件(Core API)

可配置的 Editor
import Editor from "@arcgis/core/widgets/Editor.js";

const editor = new Editor({
  view: view,
  layerInfos: [
    {
      layer: featureLayer,
      formTemplate: {
        title: "Feature Details",
        description: "Enter feature information",
        elements: [
          {
            type: "field",
            fieldName: "name",
            label: "Name",
            description: "Enter the feature name",
          },
          {
            type: "field",
            fieldName: "category",
            label: "Category",
          },
        ],
      },
      addEnabled: true,
      updateEnabled: true,
      deleteEnabled: false,
    },
  ],
});

view.ui.add(editor, "top-right");
LayerInfo 配置
属性类型默认值描述
layerFeatureLayer必填要编辑的图层
formTemplateFormTemplate表单配置
enabledbooleantrue启用图层编辑
addEnabledbooleantrue允许创建要素
updateEnabledbooleantrue允许更新要素
deleteEnabledbooleantrue允许删除要素
geometryUpdatesEnabledbooleantrue允许编辑几何
attributeUpdatesEnabledbooleantrue允许编辑属性
attachmentsOnCreateEnabledbooleantrue创建要素时允许添加附件
attachmentsOnUpdateEnabledbooleantrue更新要素时允许添加附件

要素表单

独立要素表单
import FeatureForm from "@arcgis/core/widgets/FeatureForm.js";

const form = new FeatureForm({
  container: "formDiv",
  layer: featureLayer,
  formTemplate: {
    title: "Edit Feature",
    elements: [
      {
        type: "field",
        fieldName: "name",
        label: "Name",
      },
      {
        type: "group",
        label: "Location Details",
        elements: [
          { type: "field", fieldName: "address" },
          { type: "field", fieldName: "city" },
          { type: "field", fieldName: "zip" },
        ],
      },
    ],
  },
});

// Set feature to edit
form.feature = graphic;

// Listen for submit
form.on("submit", async () => {
  if (form.valid) {
    const values = form.getValues();
    graphic.attributes = { ...graphic.attributes, ...values };
    await featureLayer.applyEdits({
      updateFeatures: [graphic],
    });
  }
});
要素表单组件
<arcgis-feature-form></arcgis-feature-form>

<script type="module">
  const featureForm = document.querySelector("arcgis-feature-form");

  // Configure the form
  featureForm.formTemplate = {
    title: "Edit Feature",
    elements: [
      { type: "field", fieldName: "name", label: "Name" },
      { type: "field", fieldName: "status", label: "Status" },
    ],
  };

  // Set feature to edit
  featureForm.feature = selectedGraphic;

  // Listen for value changes
  featureForm.addEventListener("arcgisValueChange", (event) => {
    console.log("Field changed:", event.detail.fieldName, event.detail.value);
  });

  // Listen for submit
  featureForm.addEventListener("arcgisSubmit", (event) => {
    console.log("Form submitted");
  });
</script>
带分组元素的表单
const formTemplate = {
  title: "Asset Details",
  elements: [
    {
      type: "group",
      label: "Basic Information",
      elements: [
        { type: "field", fieldName: "asset_id" },
        { type: "field", fieldName: "asset_name" },
        { type: "field", fieldName: "asset_type" },
      ],
    },
    {
      type: "group",
      label: "Maintenance",
      initialState: "collapsed",
      elements: [
        { type: "field", fieldName: "last_inspection" },
        { type: "field", fieldName: "next_inspection" },
        { type: "field", fieldName: "condition" },
      ],
    },
  ],
};
基于表达式的可见性
const formTemplate = {
  expressionInfos: [
    {
      name: "type-requires-subtype",
      expression: "$feature.type == 'complex'",
    },
  ],
  elements: [
    {
      type: "field",
      fieldName: "type",
    },
    {
      type: "field",
      fieldName: "subtype",
      visibilityExpression: "type-requires-subtype",
    },
  ],
};
表单验证
const form = new FeatureForm({
  layer: featureLayer,
  formTemplate: {
    elements: [
      {
        type: "field",
        fieldName: "email",
        label: "Email",
        requiredExpression: "email-required",
      },
    ],
    expressionInfos: [
      {
        name: "email-required",
        expression: "$feature.contact_method == 'email'",
      },
    ],
  },
});

// Available expression types on FieldElement:
// - visibilityExpression: controls field visibility
// - editableExpression: controls whether field is editable
// - requiredExpression: controls whether field is required
// - valueExpression: computes a calculated value for the field

if (form.valid) {
  // Submit
}

以编程方式编辑(applyEdits)

// Add features
const addResult = await featureLayer.applyEdits({
  addFeatures: [
    {
      attributes: {
        name: "New Feature",
        category: "Type A",
      },
      geometry: {
        type: "point",
        longitude: -118.805,
        latitude: 34.027,
      },
    },
  ],
});

// Update features
const updateResult = await featureLayer.applyEdits({
  updateFeatures: [
    {
      attributes: {
        OBJECTID: 123,
        name: "Updated Name",
      },
    },
  ],
});

// Delete features
const deleteResult = await featureLayer.applyEdits({
  deleteFeatures: [{ objectId: 123 }],
});

// Check results
addResult.addFeatureResults.forEach((result) => {
  if (result.error) {
    console.error("Add failed:", result.error.message);
  } else {
    console.log("Added feature OID:", result.objectId);
  }
});

子类型

使用子类型
const layer = new FeatureLayer({
  url: "https://services.arcgis.com/.../FeatureServer/0",
});

await layer.load();

const subtypeField = layer.subtypeField;
const subtypes = layer.subtypes;

subtypes.forEach((subtype) => {
  console.log("Code:", subtype.code);
  console.log("Name:", subtype.name);
  console.log("Default values:", subtype.defaultValues);
});
子类型组图层
import SubtypeGroupLayer from "@arcgis/core/layers/SubtypeGroupLayer.js";

const subtypeLayer = new SubtypeGroupLayer({
  url: "https://services.arcgis.com/.../FeatureServer/0",
});

await subtypeLayer.load();

// Access sublayers by subtype
subtypeLayer.sublayers.forEach((sublayer) => {
  console.log("Subtype:", sublayer.subtypeCode, sublayer.title);
  // Each sublayer can have different renderer/popup
});
支持子类型的 Editor
const editor = new Editor({
  view: view,
  layerInfos: [
    {
      layer: subtypeGroupLayer,
      addEnabled: true,
      updateEnabled: true,
    },
  ],
});

版本控制

分支版本控制概述

分支版本控制允许多个用户同时编辑相同的数据,并在协调之前避免冲突。

import VersionManagementService from "@arcgis/core/versionManagement/VersionManagementService.js";

const vms = new VersionManagementService({
  url: "https://services.arcgis.com/.../VersionManagementServer",
});

await vms.load();
console.log("Default version:", vms.defaultVersionIdentifier);
获取版本信息
const versions = await vms.getVersionInfos();
versions.forEach((v) => {
  console.log("Version:", v.versionName);
  console.log("  Owner:", v.versionOwner);
  console.log("  Access:", v.access); // public, protected, private
  console.log("  Parent:", v.parentVersionName);
});
创建、更改和删除版本
// Create new version
const newVersion = await vms.createVersion({
  versionName: "MyEditVersion",
  description: "Version for editing project X",
  access: "private",
  parentVersionName: "sde.DEFAULT",
});

// Alter version properties
await vms.alterVersion({
  versionName: "sde.MyEditVersion",
  description: "Updated description",
  access: "protected",
});

// Delete a version
await vms.deleteVersion({
  versionName: "sde.MyEditVersion",
});
切换版本
// Set version on layer at creation
const featureLayer = new FeatureLayer({
  url: "https://services.arcgis.com/.../FeatureServer/0",
  gdbVersion: "sde.MyEditVersion",
});

// Or change version dynamically
await featureLayer.load();
featureLayer.gdbVersion = "sde.AnotherVersion";
await featureLayer.refresh();
编辑会话与协调
const versionIdentifier = version.versionIdentifier;

// Start editing session
await vms.startEditing({ versionIdentifier });

// Apply edits to feature layer
await featureLayer.applyEdits(edits);

// Reconcile with parent
const reconcileResult = await vms.reconcile({
  versionIdentifier: versionIdentifier,
  abortIfConflicts: false,
  conflictDetection: "by-attribute",
  conflictResolution: "favorEditVersion",
  withPost: false,
});

if (!reconcileResult.hasConflicts) {
  await vms.post({ versionIdentifier });
}

// Stop editing
await vms.stopEditing({
  versionIdentifier: versionIdentifier,
  saveEdits: true,
});
完整的版本工作流
async function editInVersion(vms, featureLayer, edits) {
  const version = await vms.createVersion({
    versionName: `Edit_${Date.now()}`,
    description: "Temporary edit version",
    access: "private",
  });

  const versionIdentifier = version.versionIdentifier;

  try {
    featureLayer.gdbVersion = version.versionName;
    await featureLayer.refresh();

    await vms.startEditing({ versionIdentifier });
    await featureLayer.applyEdits(edits);

    const result = await vms.reconcile({
      versionIdentifier,
      abortIfConflicts: false,
      conflictResolution: "favorEditVersion",
      withPost: false,
    });

    if (result.hasConflicts) {
      throw new Error("Conflicts detected during reconcile");
    }

    await vms.post({ versionIdentifier });
    await vms.stopEditing({ versionIdentifier, saveEdits: true });
  } finally {
    featureLayer.gdbVersion = vms.defaultVersionIdentifier;
    await featureLayer.refresh();
    await vms.deleteVersion({ versionName: version.versionName });
  }
}

相关记录

查询相关记录
const relatedRecords = await layer.queryRelatedFeatures({
  outFields: ["*"],
  relationshipId: 0,
  objectIds: [selectedFeature.attributes.OBJECTID],
});

const results = relatedRecords[selectedFeature.attributes.OBJECTID];
results.features.forEach((related) => {
  console.log("Related:", related.attributes);
});

附件

在 Editor 中启用附件
const editor = new Editor({
  view: view,
  layerInfos: [
    {
      layer: featureLayer,
      attachmentsOnCreateEnabled: true,
      attachmentsOnUpdateEnabled: true,
    },
  ],
});
以编程方式处理附件
// Query attachments
const attachments = await layer.queryAttachments({
  objectIds: [featureOID],
});

// Add attachment
const formData = new FormData();
formData.append("attachment", fileBlob, "photo.jpg");
await layer.addAttachment(featureOID, formData);

// Delete attachment
await layer.deleteAttachments(featureOID, [attachmentId]);

完整示例:地图组件

<!DOCTYPE html>
<html>
  <head>
    <script src="https://js.arcgis.com/5.0/"></script>
    <script
      type="module"
      src="https://js.arcgis.com/5.0/map-components/"
    ></script>
    <style>
      html,
      body {
        height: 100%;
        margin: 0;
      }
    </style>
  </head>
  <body>
    <arcgis-map
      basemap="streets-navigation-vector"
      center="-118.805,34.027"
      zoom="13"
    >
      <arcgis-zoom slot="top-left"></arcgis-zoom>
      <arcgis-editor slot="top-right"></arcgis-editor>
    </arcgis-map>

    <script type="module">
      const FeatureLayer = await $arcgis.import(
        "@arcgis/core/layers/FeatureLayer.js",
      );

      const mapElement = document.querySelector("arcgis-map");
      const view = await mapElement.view;
      await view.when();

      const layer = new FeatureLayer({
        url: "https://services.arcgis.com/.../FeatureServer/0",
      });
      mapElement.map.add(layer);

      const editor = document.querySelector("arcgis-editor");
      editor.layerInfos = [
        {
          layer: layer,
          formTemplate: {
            title: "Edit Feature",
            elements: [
              {
                type: "group",
                label: "Details",
                elements: [
                  { type: "field", fieldName: "name", label: "Name" },
                  { type: "field", fieldName: "type", label: "Type" },
                  { type: "field", fieldName: "status", label: "Status" },
                ],
              },
            ],
          },
          attachmentsOnCreateEnabled: true,
          attachmentsOnUpdateEnabled: true,
        },
      ];
    </script>
  </body>
</html>

参考示例

  • editor-basic - Editor 组件的基本用法
  • widgets-editor-configurable - 带有 layerInfos 的可配置 Editor 微件
  • widgets-editor-subtypes - 使用子类型组图层进行编辑
  • widgets-editor-form-elements - Editor 中的表单元素配置
  • editing-applyedits - 使用 applyEdits 以编程方式编辑
  • editing-groupedfeatureform - 分组要素表单布局
  • editing-featureform-fieldvisibility - 控制字段可见性
  • changing-version - 切换地理数据库版本

常见问题

  1. 编辑权限:在检查编辑能力之前就对图层调用 applyEdits,会导致静默失败。

    // Anti-pattern: calling applyEdits without checking capabilities
    const layer = new FeatureLayer({
      url: "https://services.arcgis.com/.../FeatureServer/0",
    });
    await layer.applyEdits({ addFeatures: [newFeature] });
    
    // Correct: check capabilities before editing
    const layer = new FeatureLayer({
      url: "https://services.arcgis.com/.../FeatureServer/0",
    });
    await layer.load();
    if (
      layer.editingEnabled &&
      layer.capabilities?.editing?.supportsAddingFeatures
    ) {
      await layer.applyEdits({ addFeatures: [newFeature] });
    } else {
      console.error("Layer does not support adding features");
    }
    

    影响: applyEdits 调用会因难以理解的服务器错误而失败,或静默返回容易被忽略的错误结果。

  2. 版本锁定:编辑分支版本数据时未妥善管理会话,会导致版本锁定。

    // Anti-pattern: editing without version management session
    layer.gdbVersion = "editor1.design_v1";
    await layer.applyEdits({ updateFeatures: [updatedFeature] });
    // Version remains locked, blocking other users
    
    // Correct: use proper version management session
    await vms.startEditing({ versionIdentifier });
    layer.gdbVersion = "editor1.design_v1";
    await layer.applyEdits({ updateFeatures: [updatedFeature] });
    await vms.stopEditing({ versionIdentifier, saveEdits: true });
    

    影响: 编辑后版本仍处于锁定状态,导致其他用户无法访问或编辑该版本。

  3. 检查 applyEdits 结果:始终检查结果对象中的各个要素错误。

    const result = await layer.applyEdits({ addFeatures: [feature1, feature2] });
    result.addFeatureResults.forEach((res) => {
      if (res.error) {
        console.error("Failed to add feature:", res.error.message);
      }
    });
    
  4. 表单表达式名称:必须通过名称字符串引用表达式,而不是使用表达式文本。

    expressionInfos: [{ name: "my-expr", expression: "$feature.type == 'A'" }],
    elements: [{
      visibilityExpression: "my-expr"  // String reference to name
    }]
    
  5. 子类型字段:必须与服务中的子类型配置完全一致。

相关技能

  • 请参阅 arcgis-tables-forms,了解 FeatureTable 和 FormTemplate 输入类型。
  • 请参阅 arcgis-interaction,了解命中测试和草图绘制。
  • 请参阅 arcgis-layers,了解 FeatureLayer 配置。
  • 请参阅 arcgis-core-maps,了解自动类型转换指南。

评论

支持 Markdown