
# 地图对象实体

 [MapViewEntity](https://vjmap.com/map3d/api/classes/MapViewEntity.html) 继承于 [MeshEntity](https://vjmap.com/map3d/api/classes/MeshEntity.html), 里面包含 [MapView](https://vjmap.com/map3d/api/classes/MapView.html) 地图对象。


 ## 常用方法


 `MapViewEntity` 是由 实体`MeshEntity` 派生而来。具体 [MeshEntity](https://vjmap.com/map3d/api/classes/MeshEntity.html) 的所有属性和方法。同时可通过属性`map`访问 [MapView](https://vjmap.com/map3d/api/classes/MapView.html) 对象所有相关方法

|方法|描述|
|:---|:---|
|[mapToLocal](https://vjmap.com/map3d/api/classes/MapView.html#mapToLocal)|地图坐标转局部世界坐标|
|[localToMap](https://vjmap.com/map3d/api/classes/MapView.html#localToMap)|局部世界坐标转地图坐标|
|[mapToWorld](https://vjmap.com/map3d/api/classes/MapView.html#mapToWorld)|地图坐标转世界坐标|
|[worldToMap](https://vjmap.com/map3d/api/classes/MapView.html#worldToMap)|世界坐标转地图坐标|
|[lngLatToLocal](https://vjmap.com/map3d/api/classes/MapView.html#lngLatToLocal)|经纬度转局部世界坐标|
|[localToLngLat](https://vjmap.com/map3d/api/classes/MapView.html#localToLngLat)|局部世界坐标转经纬度|
|[lngLatToWorld](https://vjmap.com/map3d/api/classes/MapView.html#lngLatToWorld)|经纬度转世界坐标|
|[worldToLngLat](https://vjmap.com/map3d/api/classes/MapView.html#worldToLngLat)|世界坐标转经纬度|
|[lngLatToNormalize](https://vjmap.com/map3d/api/classes/MapView.html#lngLatToNormalize)|经纬度转归一化的坐标|
|[getMapBounds](https://vjmap.com/map3d/api/classes/MapView.html#getMapBounds)|获取地图范围|
|[worldResolution](https://vjmap.com/map3d/api/classes/MapView.html#worldResolution)|世界坐标中一个单位代表多少**地图单位**（= 地图范围宽度 ÷ 对应世界距离）：互联网底图下是墨卡托米，CAD 底图下是 **CAD 图形单位**（不一定是米）|
|[isWebBaseMap](https://vjmap.com/map3d/api/classes/MapView.html#isWebBaseMap)|是否为互联网地图底图 (选项中设置了mapBounds的为cad底图，没设置mapBounds的为互联网底图)|
|[setProvider](https://vjmap.com/map3d/api/classes/MapView.html#setProvider)|设置地图数据提供者|
|[setHeightProvider](https://vjmap.com/map3d/api/classes/MapView.html#setHeightProvider)|设置地图地形高度数据提供者|
|[clear](https://vjmap.com/map3d/api/classes/MapView.html#clear)|清空|
|[minZoom](https://vjmap.com/map3d/api/classes/MapView.html#minZoom)|最小缩放级别|
|[maxZoom](https://vjmap.com/map3d/api/classes/MapView.html#maxZoom)|最大缩放级别|

::: tip 提示
调用 MapViewEntity 中的方法 等同于 调用 MapViewEntity.map 中的方法

如 MapViewEntity.mapToLocal 等价于 MapViewEntity.map.mapToLocal
:::

::: tip 构造函数第二参数与多张图
`new vjmap3d.MapViewEntity(mapOptions, entityOptions?)` 的第二参数除 `MeshEntity` 选项外还有：

- `autoSetAppCurrentMapView`（默认 `true`）：加入 app 时把自己设为 `app.mapViewEntity`；
- `disableRayQuery`（默认 `true`）：给地图网格打 `userData.disableRayQuery` 标记（实体射线查询会跳过带此标记的对象）。

`vjmap3d.toWorld` / `fromWorld` / `toDist` / `fromDist` 缺省使用 `app.mapViewEntity`——同一场景加多张图时**最后加入的那张**生效，需显式传 `mapView` 参数（`toWorld`/`fromWorld` 第 3 参、`toDist`/`fromDist` 第 4 参，如 `vjmap3d.toWorld([x, y], app, mapviewEnt2)`），或直接用 `mapviewEnt2.mapToWorld(...)`。

`mapToWorld` / `lngLatToWorld` 依赖地图节点的 `matrixWorld`，请在 `addTo(app)` 之后再调用（之前得到的是未经 `baseScale` 缩放的局部坐标）。
:::


## 加载全世界地图
```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let provider = new vjmap3d.MapProvider(
    [
        
        {
            url: tdtImgUrl,
        },
        {
            url: tdtAnnoUrl
        }
    ]
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
```

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtVecUrl =
    "https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let provider = new vjmap3d.MapProvider(
    [
        
        {
            url: tdtImgUrl,
        },
        {
            url: tdtAnnoUrl
        }
    ]
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
//
let lngLat = [116.3912, 39.9073];
let worldPoint = mapviewEnt.lngLatToWorld(lngLat);
let marker2d = new vjmap3d.Marker2D({
    color: '#f00'
});
marker2d.setPosition(worldPoint);
marker2d.addTo(app);
</pre>
</mapFrame>






## 加载瓦片调试图层

```js
{
    fetchTile: (zoom, x, y) => vjmap3d.MapUtils.fetchDebugTile(zoom, x, y, "#16417C", "#39107B")
}
```
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let provider = new vjmap3d.MapProvider(
    [
        {
            fetchTile: (zoom, x, y) => vjmap3d.MapUtils.fetchDebugTile(zoom, x, y, "#16417C", "#39107B")
        },
        {
            url: tdtImgUrl,
            opacity: 0.5
        },
        {
            url: tdtAnnoUrl
        }
    ]
);

let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
</pre>
</mapFrame>




## 加载局部地图

通过指定 `rootTile` 根节点下为哪个瓦片来加载局部地图。
```js
{
    rootTile: [7, 105, 48] // 根节点下瓦片z,x,y 具体数字可通过调试瓦片得到
}
```
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtVecUrl =
    "https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let provider = new vjmap3d.MapProvider(
    [
        {
            url: tdtVecUrl
        },
        {
            url: tdtAnnoUrl
        }
    ],
    {
        rootTile: [7, 105, 48]
    }
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
//
let lngLat = [116.3912, 39.9073];
let worldPoint = mapviewEnt.lngLatToWorld(lngLat);
let marker2d = new vjmap3d.Marker2D({
    color: '#f00'
});
marker2d.setPosition(worldPoint);
marker2d.addTo(app);
// 
let isRoadWayLayer = true; // 当前是否为道路图层
let control = new vjmap3d.ButtonGroupControl({
    buttons: [
        {
            id: "grid",
            html: "影像图层",
            title: "影像图层",
            style: {
                width: "100px"
            },
            onclick: async () => {
                 if (!isRoadWayLayer) return;
                 isRoadWayLayer = false;
                 let provider = new vjmap3d.MapProvider([
                    {
                        url: tdtImgUrl
                    },
                    {
                        url: tdtAnnoUrl
                    }
                ],
                {
                    rootTile: [7, 105, 48]
                });
                mapviewEnt.setProvider(provider);
            }
        },
        {
            id: "grid",
            html: "道路图层",
            title: "道路图层",
            style: {
                width: "100px"
            },
            onclick: async () => {
                 if (isRoadWayLayer) return;
                 isRoadWayLayer = true;
                 let provider = new vjmap3d.MapProvider([
                    {
                        url: tdtVecUrl
                    },
                    {
                        url: tdtAnnoUrl
                    }
                ],
                {
                    rootTile: [7, 105, 48]
                });
                mapviewEnt.setProvider(provider);
            }
        },
        {
            id: "grid",
            html: "调试图层",
            title: "调试图层",
            style: {
                width: "100px"
            },
            onclick: async () => {
                let provider = new vjmap3d.MapProvider([
                     {
                        fetchTile: (zoom, x, y) => vjmap3d.MapUtils.fetchDebugTile(zoom, x, y, "#16417C", "#39107B")
                    },
                    {
                        url: isRoadWayLayer ? tdtVecUrl : tdtImgUrl,
                        opacity: 0.5
                    },
                    {
                        url: tdtAnnoUrl
                    }
                ],
                {
                    rootTile: [7, 105, 48]
                });
                mapviewEnt.setProvider(provider);
            }
        }
    ]
});
app.addControl(control, "top-right");
</pre>
</mapFrame>





## 加载地形数据

通过指定 `heightProvider` 指定高度地形数据

```js
// 地形高度数据
let heightProvider = new vjmap3d.MapProvider(
    [
        {
            url: terrainHeightUrl
        }
    ],
    {
        rootTile: [3, 6, 3]
    }
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    heightProvider,
    baseScale: 100,
});
```
<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtVecUrl =
    "https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
const terrainHeightUrl =
    "https://your-terrain-server/{z}/{x}/{y}.png"; // 替换为 Terrain-RGB 编码的高程瓦片服务
let provider = new vjmap3d.MapProvider(
    [
        {
            url: tdtImgUrl
        },
        {
            url: tdtAnnoUrl
        }
    ],
    {
        rootTile: [3, 6, 3]
    }
);
// 地形高度数据
let heightProvider = new vjmap3d.MapProvider(
    [
        {
            url: terrainHeightUrl
        }
    ],
    {
        rootTile: [3, 6, 3]
    }
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    heightProvider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
</pre>
</mapFrame>






## 裁剪地图 

通过 `clipPolygon`来设置裁剪多边形的范围

```js
let clipRegion = [[117.210024,40.082262],[117.105315,40.074479],[117.344299,40.13443],[117.210024,40.082262]];
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
    clipPolygon: clipRegion
});
```

数组形式只支持**单个外环**（不支持多多边形/挖洞）。需要挖洞时改传 `THREE.Shape`（在其 `holes` 中加洞），且 Shape 坐标是**归一化的 0–1 地图坐标**（不是经纬度/CAD 坐标），如下面的示例将生成一个圆环的裁剪地图
 ```js
 const circleRadius = 0.4;
const circleShape = new THREE.Shape().ellipse(
    0.5,
    0.5,
    circleRadius,
    circleRadius,
    0,
    Math.PI * 2,
    true,
    0
);
const holePath = new THREE.Path().ellipse(
    0.5,
    0.5,
    circleRadius / 2,
    circleRadius / 2,
    0,
    Math.PI * 2,
    true,
    0
);
circleShape.holes.push(holePath);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider: provider,
    baseScale: 100,
    clipPolygon: circleShape
});
mapviewEnt.addTo(app);
```


<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtVecUrl =
    "https://t3.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
const terrainHeightUrl =
    "https://your-terrain-server/{z}/{x}/{y}.png"; // 替换为 Terrain-RGB 编码的高程瓦片服务
let provider = new vjmap3d.MapProvider(
    [
        {
            url: tdtImgUrl
        },
        {
            url: tdtAnnoUrl
        }
    ],
    {
        rootTile: [7, 105, 48]
    }
);
// 地形高度数据
let heightProvider = new vjmap3d.MapProvider(
    [
        {
            url: terrainHeightUrl
        }
    ],
    {
        rootTile: [7, 105, 48]
    }
);
let beijingRegion = [[117.210024,40.082262],[117.105315,40.074479],[117.105315,40.074479],[117.102851,40.073563],[117.102235,40.073105],[117.102235,40.073105],[117.102851,40.073563],[116.999989,40.030053],[116.927924,40.054788],[116.783794,40.035093],[116.757925,39.968176],[116.786874,39.886963],[116.926076,39.835524],[116.949482,39.778529],[116.902055,39.763813],[116.90575,39.687883],[116.812128,39.616018],[116.717273,39.603572],[116.717273,39.603572],[116.720969,39.599884],[116.720969,39.599884],[116.726512,39.595274],[116.726512,39.595274],[116.703106,39.588819],[116.703106,39.588819],[116.607636,39.619705],[116.524484,39.596657],[116.440716,39.527466],[116.433325,39.44296],[116.332927,39.457744],[116.245464,39.515466],[116.204196,39.588819],[116.10195,39.576368],[116.10195,39.576368],[115.957204,39.561147],[115.910393,39.600345],[115.910393,39.600345],[115.855574,39.554689],[115.855574,39.554689],[115.846951,39.550999],[115.846951,39.550999],[115.821081,39.517312],[115.821081,39.517312],[115.752712,39.512696],[115.752712,39.512696],[115.738545,39.539464],[115.738545,39.539925],[115.738545,39.539464],[115.738545,39.539925],[115.737314,39.544078],[115.737314,39.544078],[115.723763,39.544539],[115.723763,39.544539],[115.721299,39.543617],[115.721299,39.543617],[115.721299,39.55146],[115.721299,39.55146],[115.716988,39.560225],[115.716988,39.560225],[115.699125,39.577751],[115.698509,39.577751],[115.698509,39.577751],[115.699125,39.577751],[115.698509,39.577751],[115.69543,39.579135],[115.69543,39.579135],[115.586408,39.58928],[115.478619,39.650578],[115.478619,39.650578],[115.498945,39.69617],[115.498945,39.69617],[115.443511,39.785426],[115.443511,39.785426],[115.567314,39.816224],[115.514344,39.837821],[115.522967,39.898898],[115.426264,39.95029],[115.454597,40.029595],[115.599343,40.11979],[115.73485,40.129398],[115.773038,40.176044],[115.85311,40.148609],[115.89869,40.234536],[115.968907,40.264219],[115.9184,40.354103],[115.861733,40.364589],[115.861733,40.364589],[115.779197,40.442501],[115.755792,40.540333],[115.907929,40.617133],[116.005247,40.58397],[116.088399,40.62667],[116.22021,40.744181],[116.247311,40.791762],[116.464738,40.771827],[116.334159,40.90446],[116.473977,40.895867],[116.455499,40.98084],[116.519557,40.981292],[116.519557,40.981292],[116.599013,40.974516],[116.615643,41.053072],[116.688324,41.044499],[116.677853,40.970902],[116.730208,40.897676],[116.858323,40.833423],[116.964881,40.70972],[117.110858,40.70836],[117.286401,40.660719],[117.386799,40.684317],[117.49582,40.674334],[117.389879,40.5617],[117.344299,40.582152],[117.213104,40.512136],[117.225423,40.369148],[117.309191,40.279284],[117.309807,40.279284],[117.309191,40.279284],[117.309807,40.279284],[117.389879,40.228141],[117.367089,40.172387],[117.367089,40.172844],[117.367089,40.173301],[117.367089,40.173301],[117.367089,40.172844],[117.367089,40.172387],[117.344299,40.13443],[117.210024,40.082262]];
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    heightProvider,
    baseScale: 100,
    clipPolygon: beijingRegion
});
mapviewEnt.addTo(app);
// 根据给定的经纬度置为默认地图中心点
let world = mapviewEnt.lngLatToWorld([116.3912, 39.9073]);
app.cameraControl.moveTo(world.x, 0, world.z) 
</pre>
</mapFrame>






## 加载CAD图

通过指定 `mapBounds` 传入CAD地图范围

```js
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let res = await app.svc.openMap({
    mapid: env.exampleMapId, // 地图ID,(请确保此ID已存在，可上传新图形新建ID)
    mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap3d.openMapDarkStyle()
});
if (res.error) {
    showError(res.error);
    return;
}
// 获取当前地图的可视范围
let provider = new vjmap3d.MapProvider(
    [
        {
            url: app.svc.rasterTileUrl()
        }
    ],
    {
        transparent: true
    }
);
let mapBounds = vjmap3d.GeoBounds.fromString(res.bounds)
let scale = 100;
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: scale,
    mapBounds: mapBounds.toArray()
});
mapviewEnt.addTo(app);
//  根据cad图上的一个点用marker标记出来，看是否正确
let point = mapBounds.center().toArray();
//let local = mapview.mapToLocal(point);
let world = mapviewEnt.mapToWorld(point);
let marker2d = new vjmap3d.Marker2D({
    color: '#f00'
});
marker2d.setPosition(world);
marker2d.addTo(app);
```


<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let res = await app.svc.openMap({
    mapid: env.exampleMapId, // 地图ID,(请确保此ID已存在，可上传新图形新建ID)
    mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap3d.openMapDarkStyle()
});
if (res.error) {
    showError(res.error);
    return;
}
let provider = new vjmap3d.MapProvider(
    [
        {
            url: app.svc.rasterTileUrl()
        }
    ],
    {
        transparent: true
    }
);
let mapBounds = vjmap3d.GeoBounds.fromString(res.bounds)
let scale = 100;
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: scale,
    mapBounds: mapBounds.toArray()
});
mapviewEnt.addTo(app);
//  根据cad图上的一个点用marker标记出来，看是否正确
let point = mapBounds.center().toArray();
//let local = mapview.mapToLocal(point);
let world = mapviewEnt.mapToWorld(point);
let marker2d = new vjmap3d.Marker2D({
    color: '#f00'
});
marker2d.setPosition(world);
marker2d.addTo(app);
</pre>
</mapFrame>





## 同时创建加载多个CAD图

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
const createMap = async (mapId) => {
    let res = await app.svc.openMap({
        mapid: mapId, // 地图ID,(请确保此ID已存在，可上传新图形新建ID)
        mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
        style: vjmap3d.openMapDarkStyle()
    });
    if (res.error) {
        showError(res.error);
        return;
    }
    let provider = new vjmap3d.MapProvider(
        [
            {
                url: app.svc.rasterTileUrl()
            }
        ],
        {
            transparent: true
        }
    );
    let mapBounds = vjmap3d.GeoBounds.fromString(res.bounds)
    let scale = 100;
    let mapviewEnt = new vjmap3d.MapViewEntity({
        provider,
        baseScale: scale,
        mapBounds: mapBounds.toArray()
    });
    mapviewEnt.addTo(app);
    //  根据cad图上的一个点用marker标记出来，看是否正确
    let point = mapBounds.center().toArray();
    // 转成地图的局部坐标，再把这个marker加进地图实体中，做为一个子节点。这样地图实体位置变化，他也会相应变化
    let local = mapviewEnt.mapToLocal(point);
    let marker2d = new vjmap3d.Marker2D({
        color: vjmap3d.randHtmlColor()
    });
    marker2d.setPosition(local);
    marker2d.addTo(mapviewEnt);
    return mapviewEnt;
}
let mapviewEnt1 = await createMap(env.exampleMapId);
let mapviewEnt2 = await createMap("sys_world");
mapviewEnt2.position.set(10, 5, 0);
</pre>
</mapFrame>




## 拾取CAD图上的对象

<mapFrame>
<pre>
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let res = await app.svc.openMap({
    mapid: env.exampleMapId, // 地图ID,(请确保此ID已存在，可上传新图形新建ID)
    mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap3d.openMapDarkStyle()
});
if (res.error) {
    showError(res.error);
    return;
}
let provider = new vjmap3d.MapProvider(
    [
        {
            url: app.svc.rasterTileUrl()
        }
    ],
    {
        transparent: true
    }
);
let mapBounds = vjmap3d.GeoBounds.fromString(res.bounds)
let scale = 100;
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: scale,
    mapBounds: mapBounds.toArray()
});
mapviewEnt.addTo(app);
const pickMapObject = async () => {
    let result = await app.pickPoint({
        unprojectOpts: { include: [mapviewEnt.node], exclude: [] } // 直接传数组会被忽略、退化为全场景求交
    });
    if (!result.point) return;
    console.log(result);
    let mapPoint = mapviewEnt.worldToMap(result.point);
    // @ts-ignore
    let px = result.event.originalEvent.clientX, // unproject 用浏览器视口 client 坐标(内部减去容器 left/top)
    // @ts-ignore
    py = result.event.originalEvent.clientY;
    // 获取一个像素多少地图长度
    let worldPointNextPoint = app.unproject(px, py + 1, { include: [mapviewEnt.node], exclude: [] });
    if (!worldPointNextPoint) return;
    let mapNextPoint = mapviewEnt.worldToMap(worldPointNextPoint);
    let onePixelDist = Math.max(
        Math.abs(mapPoint[0] - mapNextPoint[0]),
        Math.abs(mapPoint[1] - mapNextPoint[1])
    );
    // @ts-ignore
    let queryResult = await svc.pointQueryFeature({
        x: mapPoint[0],
        y: mapPoint[1],
        pixelsize: 5,
        pixelToGeoLength: onePixelDist
    });
    if (queryResult.result?.length > 0) {
        app.logInfo(`拾取的objectid是: ${queryResult.result[0].objectid}`, "success")
    } else {
        app.logInfo(`没有拾取到`, "warn")
    }
}
let control = new vjmap3d.ButtonGroupControl({
    buttons: [
        {
            id: "grid",
            html: "拾取地图上的对象",
            title: "拾取地图上的对象",
            style: {
                width: "130px"
            },
            onclick: async () => {
                await pickMapObject()
            }
        }
    ]
});
app.addControl(control, "top-right");
</pre>
</mapFrame>


## CAD图与互联网地图叠加显示
<mapFrame>
<pre>
const tdtImgUrl =
    "https://t2.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk=YOUR_TK";
const tdtAnnoUrl = `https://t3.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk=YOUR_TK`;
let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
let app = new vjmap3d.App(svc, {
    container: "map", // 容器id
    stat: { show: true, left: "0" },
    scene: {
        gridHelper: { visible: true } // 是否显示坐标网格
    },
    camera: {  // 相机设置
        viewHelper: { enable: true, position: "leftBottom" } // 是否显示视角指示器
    },
    control: { leftButtonPan: true } // 设置为左键用于平移 (同时右键将用于旋转) 和地图2d使用习惯一样
})
let mapId = "sys_cadcesium";
let res = await svc.openMap({
    mapid: mapId, // 地图ID
    mapopenway: vjmap3d.MapOpenWay.GeomRender, // 以几何数据渲染方式打开
    style: vjmap3d.openMapDarkStyle() // div为深色背景颜色时，这里也传深色背景样式
});
if (res.error) {
    // 如果打开出错
    showError(res.error);
    return;
}
let layer = res.layer; //图层样式名
let cadEpsg = "EPSG:4544"; // cad图的espg代号
// 增加cad的wms图层
let wmsUrl = svc.wmsTileUrl({
    mapid: mapId, // 地图id
    layers: layer, // 图层名称
    bbox: "", // bbox这里不需要
    srs: "EPSG:3857", //
    crs: cadEpsg
});
wmsUrl += "&bbox={BBOX}";
let provider = new vjmap3d.MapProvider(
    [
        {
            url: tdtImgUrl
        },
        {
            url: tdtAnnoUrl
        },
        {
            url: wmsUrl
        }
    ],
    {
        rootTile: [8, 203, 106]
    }
);
let mapviewEnt = new vjmap3d.MapViewEntity({
    provider,
    baseScale: 100,
});
mapviewEnt.addTo(app);
// 相机切换至CAD图所在的位置 
app.cameraControl.loadState({
    cameraTarget: new THREE.Vector3(6.585799392027572,-1.9140336121623333e-18, -39.579426677122015),
    cameraPosition: new THREE.Vector3(6.585799392027572,19.84534552753801, -38.00315388879424),
})
</pre>
</mapFrame>

## 地图加载3dtiles瓦片

地图加载`3dtiles`瓦片 需加载`3dtiles`插件，具体示例可参考 [加载3D瓦片](../plugin/3dtiles.md)