Skip to content

面标记

面标记用于在地图上绘制区域、多边形等面状要素。SDK 提供了简单易用的 API 来添加和管理面标记。

添加面标记

基础用法

javascript
// 添加一个简单的多边形
const featureId = sdk.addPolygon(
  [
    [
      [116.3974, 39.9093], // 第一个点
      [121.4737, 39.9093], // 第二个点
      [121.4737, 31.2304], // 第三个点
      [116.3974, 31.2304], // 第四个点
      [116.3974, 39.9093], // 闭合多边形(回到起点)
    ],
  ],
  { name: "区域" }, // 属性
);

console.log("面标记ID:", featureId);

带洞的多边形

javascript
// 添加带洞的多边形(外环 + 内环)
const featureId = sdk.addPolygon(
  [
    // 外环
    [
      [116.3974, 39.9093],
      [121.4737, 39.9093],
      [121.4737, 31.2304],
      [116.3974, 31.2304],
      [116.3974, 39.9093],
    ],
    // 内环(洞)
    [
      [118.0, 36.0],
      [119.0, 36.0],
      [119.0, 35.0],
      [118.0, 35.0],
      [118.0, 36.0],
    ],
  ],
  { name: "带洞的区域" },
);

自定义样式

javascript
// 添加带自定义样式的面
const featureId = sdk.addPolygon(
  [
    [
      [116.3974, 39.9093],
      [121.4737, 39.9093],
      [121.4737, 31.2304],
      [116.3974, 31.2304],
      [116.3974, 39.9093],
    ],
  ],
  { name: "区域", type: "area" },
  {
    paint: {
      "fill-color": "#ff0000",        // 填充颜色
      "fill-opacity": 0.5,            // 填充透明度
      "fill-outline-color": "#000000", // 轮廓颜色
      "fill-pattern": null,           // 填充图案(需要sprite)
    },
  }
);

批量添加面标记

使用 GeoJSON 数据

javascript
// 添加多个面
const geojson = {
  type: "FeatureCollection",
  features: [
    {
      type: "Feature",
      geometry: {
        type: "Polygon",
        coordinates: [
          [
            [116.3974, 39.9093],
            [121.4737, 39.9093],
            [121.4737, 31.2304],
            [116.3974, 31.2304],
            [116.3974, 39.9093],
          ],
        ],
      },
      properties: { name: "区域1" },
    },
    {
      type: "Feature",
      geometry: {
        type: "Polygon",
        coordinates: [
          [
            [113.2644, 23.1291],
            [120.1551, 23.1291],
            [120.1551, 30.2741],
            [113.2644, 30.2741],
            [113.2644, 23.1291],
          ],
        ],
      },
      properties: { name: "区域2" },
    },
  ],
};

sdk.addGeoJSON("areas-source", geojson);

// 添加图层
sdk.addLayer({
  id: "areas-layer",
  source: "areas-source",
  type: "fill",
  paint: {
    "fill-color": "#007bff",
    "fill-opacity": 0.5,
  },
});

循环添加

javascript
const areas = [
  {
    name: "区域1",
    coordinates: [
      [
        [116.3974, 39.9093],
        [121.4737, 39.9093],
        [121.4737, 31.2304],
        [116.3974, 31.2304],
        [116.3974, 39.9093],
      ],
    ],
  },
  {
    name: "区域2",
    coordinates: [
      [
        [113.2644, 23.1291],
        [120.1551, 23.1291],
        [120.1551, 30.2741],
        [113.2644, 30.2741],
        [113.2644, 23.1291],
      ],
    ],
  },
];

areas.forEach((area) => {
  sdk.addPolygon(
    area.coordinates,
    { name: area.name },
    {
      paint: {
        "fill-color": "#007bff",
        "fill-opacity": 0.5,
      },
    }
  );
});

更新面标记

更新形状

javascript
// 获取要素并更新坐标
const feature = sdk.geoJSONManager.getFeature(featureId);
if (feature) {
  // 更新坐标
  feature.geometry.coordinates = [
    [
      [116.3974, 39.9093],
      [121.4737, 39.9093],
      [121.4737, 31.2304],
      [116.3974, 31.2304],
      [116.3974, 39.9093],
    ],
  ];
  
  // 更新源数据
  sdk.getSource("source-id").setData(updatedGeoJSON);
}

更新样式

javascript
// 更新面样式
sdk.updateLayerStyle("polygon-layer", {
  paint: {
    "fill-color": "#ff0000",
    "fill-opacity": 0.8,
  },
});

添加/删除顶点

javascript
// 添加顶点
function addVertexToPolygon(featureId, ringIndex, newVertex) {
  const feature = sdk.geoJSONManager.getFeature(featureId);
  if (feature && feature.geometry.type === "Polygon") {
    const ring = feature.geometry.coordinates[ringIndex];
    // 在倒数第二个位置插入(保持闭合)
    ring.splice(-1, 0, newVertex);
    sdk.getSource("source-id").setData(updatedGeoJSON);
  }
}

// 删除顶点
function removeVertexFromPolygon(featureId, ringIndex, vertexIndex) {
  const feature = sdk.geoJSONManager.getFeature(featureId);
  if (feature && feature.geometry.type === "Polygon") {
    const ring = feature.geometry.coordinates[ringIndex];
    // 不能删除最后一个点(闭合点)
    if (ring.length > 4 && vertexIndex < ring.length - 1) {
      ring.splice(vertexIndex, 1);
      sdk.getSource("source-id").setData(updatedGeoJSON);
    }
  }
}

删除面标记

删除单个面

javascript
// 方法1:通过图层ID删除
sdk.removeLayer(featureId);

// 方法2:通过 GeoJSONManager 删除
sdk.geoJSONManager.removeFeature(featureId);

删除多个面

javascript
const featureIds = ["polygon-1", "polygon-2", "polygon-3"];

featureIds.forEach((id) => {
  sdk.removeLayer(id);
});

清空所有面标记

javascript
// 删除整个图层
sdk.removeLayer("polygon-layer");

// 清空源数据
sdk.getSource("source-id").setData({
  type: "FeatureCollection",
  features: [],
});

面标记交互

点击事件

javascript
// 监听面标记点击
sdk.on("click", "polygon-layer", (e) => {
  const feature = e.features[0];
  console.log("点击的面:", feature.properties);
  
  // 显示弹窗
  const popup = new navMap.Popup()
    .setLngLat(e.lngLat)
    .setHTML(`<h3>${feature.properties.name}</h3>`)
    .addTo(sdk.map);
});

悬停效果

javascript
// 鼠标悬停时高亮
sdk.on("mouseenter", "polygon-layer", (e) => {
  sdk.getCanvas().style.cursor = "pointer";
  
  // 改变样式
  sdk.setPaintProperty("polygon-layer", "fill-color", "#ff0000");
  sdk.setPaintProperty("polygon-layer", "fill-opacity", 0.8);
});

sdk.on("mouseleave", "polygon-layer", () => {
  sdk.getCanvas().style.cursor = "";
  
  // 恢复样式
  sdk.setPaintProperty("polygon-layer", "fill-color", "#007bff");
  sdk.setPaintProperty("polygon-layer", "fill-opacity", 0.5);
});

使用 ClickManager

javascript
// 使用 ClickManager 简化点击处理
sdk.clickObj.clickPopup(
  "polygon-layer",
  (feature) => {
    const area = calculateArea(feature.geometry.coordinates);
    return `<h3>${feature.properties.name}</h3>
            <p>面积: ${area.toFixed(2)} km²</p>`;
  },
  {
    anchor: "bottom",
    closeButton: true,
  }
);

样式配置

基础样式属性

javascript
sdk.addPolygon(
  coordinates,
  { name: "区域" },
  {
    paint: {
      "fill-color": "#ff0000",         // 填充颜色
      "fill-opacity": 0.5,               // 填充透明度
      "fill-outline-color": "#000000",  // 轮廓颜色
      "fill-pattern": null,             // 填充图案
      "fill-translate": [0, 0],         // 平移
      "fill-translate-anchor": "map",   // 平移锚点
    },
  }
);

轮廓线样式

javascript
// 添加轮廓线图层
sdk.addLayer({
  id: "polygon-outline",
  source: "polygon-source",
  type: "line",
  paint: {
    "line-color": "#000000",
    "line-width": 2,
    "line-opacity": 1.0,
    "line-dasharray": [2, 2], // 虚线轮廓
  },
});

动态样式

javascript
// 根据属性值动态设置颜色
sdk.addLayer({
  id: "dynamic-polygons",
  source: "polygons-source",
  type: "fill",
  paint: {
    "fill-opacity": 0.5,
    "fill-color": [
      "case",
      ["==", ["get", "type"], "restricted"],
      "#ff0000",  // 限制区:红色
      ["==", ["get", "type"], "controlled"],
      "#0000ff",  // 管制区:蓝色
      "#00ff00",  // 其他:绿色
    ],
  },
});

根据缩放级别调整透明度

javascript
sdk.addLayer({
  id: "zoom-polygons",
  source: "polygons-source",
  type: "fill",
  paint: {
    "fill-color": "#007bff",
    "fill-opacity": [
      "interpolate",
      ["linear"],
      ["zoom"],
      5, 0.2,  // 缩放级别5时透明度0.2
      10, 0.5, // 缩放级别10时透明度0.5
      15, 0.8, // 缩放级别15时透明度0.8
    ],
  },
});

填充图案

javascript
// 使用填充图案(需要先加载sprite)
sdk.addLayer({
  id: "pattern-polygons",
  source: "polygons-source",
  type: "fill",
  paint: {
    "fill-pattern": "pattern-name", // sprite中的图案名称
    "fill-opacity": 0.8,
  },
});

几何计算

计算面积

javascript
function calculateArea(coordinates) {
  // 使用 turf.js 或其他几何计算库
  const polygon = turf.polygon(coordinates);
  return turf.area(polygon); // 返回平方米
}

// 使用示例
sdk.on("click", "polygon-layer", (e) => {
  const feature = e.features[0];
  const area = calculateArea(feature.geometry.coordinates);
  console.log("面积:", area, "平方米");
});

计算中心点

javascript
function getCentroid(coordinates) {
  const polygon = turf.polygon(coordinates);
  const centroid = turf.centroid(polygon);
  return centroid.geometry.coordinates;
}

// 使用示例
const center = getCentroid(polygonCoordinates);
sdk.flyTo({
  center: center,
  zoom: 12,
});

判断点是否在多边形内

javascript
function pointInPolygon(point, polygonCoordinates) {
  const pt = turf.point(point);
  const polygon = turf.polygon(polygonCoordinates);
  return turf.booleanPointInPolygon(pt, polygon);
}

// 使用示例
const isInside = pointInPolygon([116.3974, 39.9093], polygonCoordinates);
console.log("点是否在多边形内:", isInside);

完整示例

javascript
let sdk;

async function initMap() {
  sdk = new navMap.MapSDK({
    container: "map",
    center: [116.39, 39.9],
    zoom: 10,
  });

  sdk.on("loadComplete", () => {
    setupPolygonMarkers();
  });
}

function setupPolygonMarkers() {
  // 1. 添加单个面
  const polygonId = sdk.addPolygon(
    [
      [
        [116.3974, 39.9093],
        [121.4737, 39.9093],
        [121.4737, 31.2304],
        [116.3974, 31.2304],
        [116.3974, 39.9093],
      ],
    ],
    { name: "区域", type: "area" },
    {
      paint: {
        "fill-color": "#ff0000",
        "fill-opacity": 0.5,
        "fill-outline-color": "#000000",
      },
    }
  );

  // 2. 添加轮廓线
  sdk.addLayer({
    id: `${polygonId}-outline`,
    source: polygonId,
    type: "line",
    paint: {
      "line-color": "#000000",
      "line-width": 2,
    },
  });

  // 3. 批量添加面
  const areas = [
    {
      name: "区域1",
      coordinates: [
        [
          [116.3974, 39.9093],
          [121.4737, 39.9093],
          [121.4737, 31.2304],
          [116.3974, 31.2304],
          [116.3974, 39.9093],
        ],
      ],
    },
  ];

  areas.forEach((area) => {
    sdk.addPolygon(
      area.coordinates,
      { name: area.name, type: "area" },
      {
        paint: {
          "fill-color": "#007bff",
          "fill-opacity": 0.5,
        },
      }
    );
  });

  // 4. 添加点击事件
  sdk.clickObj.clickPopup(
    polygonId,
    (feature) => {
      const area = calculateArea(feature.geometry.coordinates);
      return `<h3>${feature.properties.name}</h3>
              <p>类型: ${feature.properties.type}</p>
              <p>面积: ${(area / 1000000).toFixed(2)} km²</p>`;
    },
    {
      anchor: "bottom",
      closeButton: true,
    }
  );

  // 5. 添加悬停效果
  sdk.on("mouseenter", polygonId, () => {
    sdk.setPaintProperty(polygonId, "fill-color", "#00ff00");
    sdk.setPaintProperty(polygonId, "fill-opacity", 0.8);
  });

  sdk.on("mouseleave", polygonId, () => {
    sdk.setPaintProperty(polygonId, "fill-color", "#ff0000");
    sdk.setPaintProperty(polygonId, "fill-opacity", 0.5);
  });
}

initMap();

注意事项

  1. 坐标顺序:坐标格式为 [经度, 纬度],注意顺序
  2. 闭合多边形:多边形的第一个点和最后一个点必须相同
  3. 环的方向:外环逆时针,内环(洞)顺时针
  4. 性能优化:大量面标记时考虑使用 LOD 配置
  5. 样式时机:确保在地图 loadComplete 后再添加面标记
  6. 几何计算:复杂几何计算建议使用 turf.js 等专业库