? Openlayers是一個基于Javacript開發(fā),免費、開源的前端地圖開發(fā)庫,使用它,可以很容易的開發(fā)出WebGIS系統(tǒng)。目前Openlayers支持地圖瓦片、矢量數(shù)據(jù)等眾多地圖數(shù)據(jù)格式,支持比較完整的地圖交互操作。目前OpenLayers已經(jīng)成為一個擁有眾多開發(fā)者和幫助社區(qū)的成熟、流行的框架,在國內(nèi)外的GIS相關(guān)行業(yè)中得到了廣泛的應(yīng)用。
成都創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比建平網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式建平網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋建平地區(qū)。費用合理售后完善,10年實體公司更值得信賴。
openlayers 官網(wǎng)地址 https://openlayers.org/
openlayers 源碼地址 https://github.com/openlayers/openlayers
// 地圖服務(wù)對象,調(diào)用唯杰地圖服務(wù)打開地圖,獲取地圖的元數(shù)據(jù)
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 打開地圖
let mapId = "sys_zp";
let res = await svc.openMap({
mapid: mapId, // 地圖ID
mapopenway: vjmap.MapOpenWay.GeomRender, // 以幾何數(shù)據(jù)渲染方式打開
style: vjmap.openMapDarkStyle() // div為深色背景顏色時,這里也傳深色背景樣式
})
if (res.error) {
// 如果打開出錯
message.error(res.error)
}
// 獲取地圖范圍
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
//自定義投影參數(shù)
let cadProjection = new ol.proj.Projection({
// extent用于確定縮放級別
extent: mapBounds.toArray(),
units: 'm'
});
// 設(shè)置每級的分辨率
let resolutions= [];
for(let i = 0; i < 25; i++) {
resolutions.push(mapBounds.width() / (512 * Math.pow(2, i - 1)))
}
// 增加自定義的cad的坐標(biāo)系
ol.proj.addProjection(cadProjection);
// 創(chuàng)建openlayer的地圖對象
let map = new ol.Map({
target: 'map', // div的id
view: new ol.View({
center: mapBounds.center().toArray(), // 地圖中心點
projection: cadProjection, // 剛自定義的cad的坐標(biāo)系
resolutions:resolutions, // 分辨率
zoom: 2// 初始縮放級別
})
});
// 增加一個瓦片圖層
let layer = new ol.layer.Tile({
// 增加一個瓦片數(shù)據(jù)源
source: new ol.source.TileImage({
url: svc.rasterTileUrl() // 唯杰地圖服務(wù)提供的cad的柵格瓦片服務(wù)地址
})
});
// 在地圖中增加上面的瓦片圖層
map.addLayer(layer);
// 增加一個矢量瓦片圖層
let layer = new ol.layer.VectorTile({
// 增加一個瓦片數(shù)據(jù)源
source: new ol.source.VectorTile({
projection: cadProjection,
format: new ol.format.MVT(),
url: svc.vectorTileUrl() // 唯杰地圖服務(wù)提供的cad的矢量瓦片服務(wù)地址
}),
style: createVjMapVectorStyle(ol.style.Style, ol.style.Fill, ol.style.Stroke, ol.style.Circle)
});
// 在地圖中增加上面的瓦片圖層
map.addLayer(layer);
const highlight_ent = async co => {
vectorSource.clear();
let res = await svc.pointQueryFeature({
x: co[0],
y: co[1],
zoom: map.getView().getZoom(),
fields: ""
}, pt => {
// 查詢到的每個點進行坐標(biāo)處理回調(diào)
return mapPrj.fromMercator(pt);// 轉(zhuǎn)成cad的坐標(biāo)
})
if (res && res.result && res.result.length > 0) {
let features = [];
for (let ent of res.result) {
if (ent.geom && ent.geom.geometries) {
let clr = vjmap.entColorToHtmlColor(ent.color);
for (let g = 0; g < ent.geom.geometries.length; g++) {
features.push({
type: "Feature",
properties: {
objectid: ent.objectid + "_" + g,
color: clr,
alpha: ent.alpha / 255,
lineWidth: 1,
name: ent.name,
isline: ent.isline,
layerindex: ent.layerindex
},
geometry: ent.geom.geometries[g]
})
}
// 選擇提示
let content = `feature: ${ent.objectid}; layer: ${cadLayers[ent.layerindex].name}; type: ${ent.name}`
message.info({ content, key: "info", duration: 3});
}
}
geojsonObject.features = features;
if (geojsonObject.features.length > 0) {
vectorSource.addFeatures( new ol.format.GeoJSON().readFeatures(geojsonObject, {dataProjection: cadProjection}))
}
}
};
// 地圖服務(wù)對象,調(diào)用唯杰地圖服務(wù)打開地圖,獲取地圖的元數(shù)據(jù)
let svc = new vjmap.Service(env.serviceUrl, env.accessToken)
// 上傳dwg文件
const uploadDwgFile = async file => {
message.info("正在上傳圖形,請稍候", 2);
let res = await svc.uploadMap(file); // 上傳地圖
// 輸入圖id
let mapid = prompt("請輸入圖名稱ID", res.mapid);
res.mapid = mapid;
res.mapopenway = vjmap.MapOpenWay.GeomRender; // 幾何渲染,內(nèi)存渲染用vjmap.MapOpenWay.Memory
res.isVector = false; // 使用柵格瓦片
res.style = vjmap.openMapDarkStyle(); // 深色樣式,淺色用openMapDarkStyle
message.info("正在打開圖形,請稍候,第一次打開時根據(jù)圖的大小可能需要幾十秒至幾分鐘不等", 5);
let data = await svc.openMap(res); // 打開地圖
if (data.error) {
message.error(data.error)
return;
}
openMap(data);
}
// 切換圖層
const switchLayer = async layers => {
let res = await svc.cmdSwitchLayers(layers); // 調(diào)用唯杰服務(wù)切換圖層,返回圖層id {layerid: "xxxx"}
let source = layer.getSource();
// 重新設(shè)置新的唯杰地圖服務(wù)提供的cad的柵格瓦片服務(wù)地址
source.setUrl(svc.rasterTileUrl());
// 刷新
source.refresh();
}
const switchToMapId = async (mapId)=> {
let res = await svc.openMap({
mapid: mapId, // 地圖ID
mapopenway: vjmap.MapOpenWay.GeomRender, // 以幾何數(shù)據(jù)渲染方式打開
style: vjmap.openMapDarkStyle() // div為深色背景顏色時,這里也傳深色背景樣式
})
if (res.error) {
// 如果打開出錯
message.error(res.error)
return;
}
// 獲取地圖范圍
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
//自定義投影參數(shù)
let cadProjection = new ol.proj.Projection({
// extent用于確定縮放級別
extent: mapBounds.toArray(),
units: 'm'
});
// 設(shè)置每級的分辨率
let resolutions= [];
for(let i = 0; i < 25; i++) {
resolutions.push(mapBounds.width() / (512 * Math.pow(2, i - 1)))
}
// 增加自定義的cad的坐標(biāo)系
ol.proj.addProjection(cadProjection);
// 重新創(chuàng)建openlayer的地圖對象
map = new ol.Map({
target: createNewMapDivId(), // div的id
view: new ol.View({
center: mapBounds.center().toArray(), // 地圖中心點
projection: cadProjection, // 剛自定義的cad的坐標(biāo)系
resolutions:resolutions, // 分辨率
zoom: 2 // 初始縮放級別
})
});
// 增加一個瓦片圖層
let layer = new ol.layer.Tile({
// 增加一個瓦片數(shù)據(jù)源
source: new ol.source.TileImage({
url: svc.rasterTileUrl() // 唯杰地圖服務(wù)提供的cad的柵格瓦片服務(wù)地址
})
});
// 在地圖中增加上面的瓦片圖層
map.addLayer(layer);
map.on('click', (e) => message.info({content: `您點擊的坐標(biāo)為: ${JSON.stringify(e.coordinate)}`, key: "info", duration: 3}));
}
let curIsDarkTheme = true;
const switchToDarkTheme = async () => {
if (curIsDarkTheme) return;
curIsDarkTheme = true;
document.body.style.background = "#022B4F"; // 背景色改為深色
await updateStyle(curIsDarkTheme)
}
const switchToLightTheme = async () => {
if (!curIsDarkTheme) return;
curIsDarkTheme = false;
document.body.style.backgroundImage = "linear-gradient(rgba(255, 255, 255, 1), rgba(233,255,255, 1), rgba(233,255,255, 1))"
await updateStyle(curIsDarkTheme)
}
const updateStyle = async (isDarkTheme) => {
style.backcolor = isDarkTheme ? 0 : 0xFFFFFF;//深色為黑色,淺色為白色
let res = await svc.cmdUpdateStyle(style);
let source = layer.getSource();
// 重新設(shè)置新的唯杰地圖服務(wù)提供的cad的柵格瓦片服務(wù)地址
source.setUrl(svc.rasterTileUrl());
// 刷新
source.refresh();
}
通過修改CAD地圖后臺樣式數(shù)據(jù)自定義地圖
// 更改樣式
const expressionList = [] ;// 表達式數(shù)組
const updateStyle = async (style) => {
let res = await svc.cmdUpdateStyle({
name: "customStyle2",
backcolor: 0,
expression:expressionList.join("\n"),
...style
});
let source = layer.getSource();
// 重新設(shè)置新的唯杰地圖服務(wù)提供的cad的柵格瓦片服務(wù)地址
source.setUrl(svc.rasterTileUrl());
// 刷新
source.refresh();
}
// 表達式語法和變量請參考
// 服務(wù)端條件查詢和表達式查詢 https://vjmap.com/guide/svrStyleVar.html
// 服務(wù)端渲染表達式語法 https://vjmap.com/guide/expr.html
// 修改顏色 紅color.r, 綠color.g, 藍color.b, 透明度color.a,如果輸入了級別的話,表示此級別及以上的設(shè)置
const modifyColor = (color, zoom) => {
let result = "";
let z = Number.isInteger(zoom) ? `[${zoom + 1}]` : '';
if ("r" in color) result += `gOutColorRed${z}:=${color.r};`;
if ("g" in color) result += `gOutColorGreen${z}:=${color.g};`;
if ("b" in color) result += `gOutColorBlue${z}:=${color.b};`;
if ("a" in color) result += `gOutColorAlpha${z}:=${color.a};`;
return result;
}
對多個cad圖進行圖層開關(guān)裁剪旋轉(zhuǎn)縮放處理后合并成一個新的cad圖
// 組合成新的圖,將sys_world圖進行一定的處理后,再與sys_hello進行合成,生成新的地圖文件名
let rsp = await svc.composeNewMap([
{
mapid: "sys_world", // 地圖id
// 下面的參數(shù)可以根據(jù)實際需要來設(shè)置,可以對圖層,范圍,坐標(biāo)轉(zhuǎn)換來進行處理
layers: ["經(jīng)緯度標(biāo)注","COUNTRY"], // 要顯示的圖層名稱列表
//clipbounds: [., 9040.0, ., 4445.], // 要顯示的范圍
//fourParameter: [0,0,1,0] // 對地圖進行四參數(shù)轉(zhuǎn)換計算
},
{
mapid: "sys_hello"
}
])
if (!rsp.status) {
message.error(rsp.error)
}
// 返回結(jié)果為
/*
{
"fileid": "pec9c5f73f1d",
"mapdependencies": "sys_world||sys_hello",
"mapfrom": "sys_world&&v1&&&&0&&&&&&&&&&00A0&&10||sys_hello&&v1&&&&0&&&&&&&&&&&&2",
"status": true
}
*/
// 實體類型ID和名稱映射
const { entTypeIdMap } = await svc.getConstData();
const getTypeNameById = name => {
for(let id in entTypeIdMap) {
if (entTypeIdMap[id] == name) {
return id
}
}
}
const queryTextAndDrawBounds = async () => {
let queryTextEntTypeId = getTypeNameById("AcDbText"); // 單行文字
let queryMTextEntTypeId = getTypeNameById("AcDbMText"); // 多行文字
let queryAttDefEntTypeId = getTypeNameById("AcDbAttributeDefinition"); // 屬性定義文字
let queryAttEntTypeId = getTypeNameById("AcDbAttribute"); // 屬性文字
let query = await svc.conditionQueryFeature({
condition: `name='${queryTextEntTypeId}' or name='${queryMTextEntTypeId}' or name='${queryAttDefEntTypeId}' or name='${queryAttEntTypeId}'`, // 只需要寫sql語句where后面的條件內(nèi)容,字段內(nèi)容請參考文檔"服務(wù)端條件查詢和表達式查詢"
fields: "",
limit: //設(shè)置很大,相當(dāng)于把所有的圓都查出來。不傳的話,默認只能取100條
}, pt => {
// 查詢到的每個點進行坐標(biāo)處理回調(diào)
return mapPrj.fromMercator(pt);// 轉(zhuǎn)成cad的坐標(biāo)
})
if (query.error) {
message.error(query.error)
} else {
message.info(`查詢到符合的記數(shù)條數(shù):${query.recordCount}`)
if (query.recordCount > 0) {
let features = [];
for(var i = 0; i < query.recordCount; i++) {
let bounds = vjmap.getEnvelopBounds(query.result[i].envelop, mapPrj);
let clr = vjmap.entColorToHtmlColor(query.result[i].color); // 實體顏色轉(zhuǎn)html顏色(
features.push({
type: "Feature",
properties: {
name: "objectid:" + query.result[i].objectid,
color: clr
},
geometry: {
'type': 'Polygon',
'coordinates': [
bounds.toPointArray(),
],
}
})
}
if (!vectorSource) {
// 如果之前沒有高亮矢量圖層
addHighLightLayer();
}
vectorSource.clear();
let geojsonObject = {
'type': 'FeatureCollection',
'features': features
}
// 修改矢量數(shù)據(jù)源數(shù)據(jù)
vectorSource.addFeatures( new ol.format.GeoJSON().readFeatures(geojsonObject, {dataProjection: cadProjection}))
}
}
}
const source = new ol.source.Vector({wrapX: false});
const vector = new ol.layer.Vector({
source: source,
});
map.addLayer(vector);
let draw; // global so we can remove it later
function addInteraction(value) {
map.removeInteraction(draw);
if (value !== 'None') {
draw = new ol.interaction.Draw({
source: source,
type: value,
});
map.addInteraction(draw);
}
}
addInteraction('Point');
// 增加高德地圖底圖
let gdlayer;
const addGaodeMap = async (isRoadway) => {
const tileUrl = svc.webMapUrl({
tileCrs: "gcj02",
tileUrl: isRoadway ? [
"https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}"
] :
/* 如果用影像 */
[
"https://webst0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=6&x={x}&y={y}&z={z}",
"https://webst0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}"
],
tileSize: 256,
tileRetina: 1,
tileMaxZoom: 18,
tileShards: "1,2,3,4",
tileToken: "",
tileFlipY: false,
mapbounds: res.bounds,
srs: "EPSG:4527",// 可通過前兩位獲取 vjmap.transform.getEpsgParam(vjmap.transform.EpsgCrsTypes.CGCS2000, 39).epsg
// 因為sys_cad2000這個圖只有6位,沒有帶系。需要在坐標(biāo)轉(zhuǎn)換前平移下帶系 https://blog.csdn.net/thinkpang/article/details/
fourParameterBefore: ",0,1,0"
})
// 增加一個瓦片圖層
gdlayer = new ol.layer.Tile({
// 增加一個瓦片數(shù)據(jù)源
source: new ol.source.TileImage({
url: tileUrl
})
});
gdlayer.setZIndex(-1);
// 在地圖中增加上面的瓦片圖層
map.addLayer(gdlayer);
// cad坐標(biāo)與高德坐標(biāo)相互轉(zhuǎn)換示例
let webCo = await cad2webCoordinate(center, false); // cad轉(zhuǎn)高德
let cadCo = await web2cadCoordinate(webCo, false); // 高德轉(zhuǎn)cad
console.log(center, webCo, cadCo)
}
let cadEpsg = "EPSG:4544";// cad圖的espg代號
// 增加cad的wms圖層
let wmsUrl = svc.wmsTileUrl({
mapid: mapId, // 地圖id
layers: layer, // 圖層名稱
bbox: '', // bbox這里不需要
srs: "EPSG:3857", //
crs: cadEpsg
})
function getQueryStringArgs(url) {
let theRequest = {};
let idx = url.indexOf("?");
if (idx != -1) {
let str = url.substr(idx + 1);
let strs = str.split("&");
for (let i = 0; i < strs.length; i++) {
let items = strs[i].split("=");
theRequest[items[0]] = items[1];
}
}
return theRequest;
}
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
// cad圖坐標(biāo)轉(zhuǎn)web wgs84坐標(biāo)
const cadToWebCoordinate = async point => {
let co = await svc.cmdTransform(cadEpsg, "EPSG:4326", point);
return co[0]
}
// cad轉(zhuǎn)wgs84經(jīng)緯度
let boundsMin = await cadToWebCoordinate(mapBounds.min);
let boundsMax = await cadToWebCoordinate(mapBounds.max);
// wgs84經(jīng)緯度轉(zhuǎn)墨卡托
boundsMin = vjmap.Projection.lngLat2Mercator(boundsMin);
boundsMax = vjmap.Projection.lngLat2Mercator(boundsMax);
// 在openlayer中增加wms圖層
map.addLayer(new ol.layer.Tile({
// 范圍
extent: [boundsMin[0], boundsMin[1], boundsMax[0], boundsMax[1]],
source: new ol.source.TileWMS({
url: wmsUrl.substr(0, wmsUrl.indexOf("?")),
params: {...getQueryStringArgs(wmsUrl),'TILED': true}
}),
}))
// cad上面的點坐標(biāo)
let cadPoints = [
vjmap.geoPoint([., .,]),
vjmap.geoPoint([., .]),
vjmap.geoPoint([.0, .]),
vjmap.geoPoint([.0, .])
];
// 在互聯(lián)網(wǎng)圖上面拾取的與上面的點一一對應(yīng)的坐標(biāo)(wgs84坐標(biāo))
let webPoints = [
vjmap.geoPoint([116., 39.]),
vjmap.geoPoint([116., 39.]),
vjmap.geoPoint([116., 39.]),
vjmap.geoPoint([116., 39.])
]
// 通過坐標(biāo)參數(shù)求出四參數(shù)
let epsg3857Points = webPoints.map(w => vjmap.geoPoint(vjmap.Projection.lngLat2Mercator(w)));
let param = vjmap.coordTransfromGetFourParamter(epsg3857Points, cadPoints , false); // 這里考慮旋轉(zhuǎn)
let fourparam = [param.dx, param.dy, param.scale, param.rotate]
// wms圖層地址
const getCadWmsUrl = (transparent) => {
let wmsUrl = svc.wmsTileUrl({
mapid: mapId, // 地圖id
layers: layer, // 圖層名稱
bbox: '', // bbox這里不需要
fourParameter: fourparam,
transparent: transparent,
backgroundColor: 'rgba(240, 255, 255)' // 不透明時有效
})
return wmsUrl
}
let mapBounds = vjmap.GeoBounds.fromString(res.bounds);
let cadPrj = new vjmap.GeoProjection(mapBounds);
// cad圖坐標(biāo)轉(zhuǎn)3857坐標(biāo)
const cadToWebCoordinate = point => {
// 再調(diào)用四參數(shù)反算求出web的坐標(biāo)
return vjmap.coordTransfromByInvFourParamter(vjmap.geoPoint(point), param)
}
// 3857轉(zhuǎn)cad圖坐標(biāo)
const webToCadCoordinate = point => {
return vjmap.coordTransfromByFourParamter(vjmap.geoPoint(point), param)
}
let wmsLayer;
const addWmsLayer = async (transparent)=> {
removeWmsLayer();
let wmsUrl = getCadWmsUrl(transparent);
wmsLayer = new ol.layer.Tile({
// 范圍
extent: bounds.toArray(),
source: new ol.source.TileWMS({
url: wmsUrl.substr(0, wmsUrl.indexOf("?")),
params: {...getQueryStringArgs(wmsUrl),'TILED': true}
}),
});
// 在openlayer中增加wms圖層
map.addLayer(wmsLayer);
}
可點擊 https://vjmap.com/demo/#/demo/map/openlayers/01olraster 在線體驗上面功能
如果需要用openlayers來加載CAD圖進行開發(fā),請參考示例 https://vjmap.com/demo/#/demo/map/openlayers/01olraster
如果需要用leaflet來加載CAD圖進行開發(fā),請參考示例 https://vjmap.com/demo/#/demo/map/leaflet/01leafletraster
如果需要用maptalks來加載CAD圖進行開發(fā),請參考示例 https://vjmap.com/demo/#/demo/map/maptalks/01maptalksraster
如何基于vue3來開發(fā)openlayers應(yīng)用,可查看此開源代碼 https://github.com/MelihAltintas/vue3-openlayers
如何基于vue2來開發(fā)openlayers應(yīng)用,可查看此開源代碼 https://github.com/ghettovoice/vuelayers