一般來說使用 ol 繪製地圖時從 gis 服務中獲取的貼片地圖可以是為所需的設計切好行政區劃形狀的,但是遇到提供的服務不太好去修改這個形狀的時候可能就需要前端來將地圖來裁切加工。
一般來說行政區劃形狀可以通過 gis 服務獲取或者使用靜態 geojson 資源獲取一個面要素數據,我們可以通過面要素數據來將所需的形狀切出來,但是 openlayer 沒有現成的 api 來完成。這時候我們就需要用到 canvas 的 globalCompositeOperation 來完成。
首先保證你的 layer 並不是 WebGL 層,然後我們需要定義好基礎圖層baseLayer
作為需要裁切的目標。
import { Tile } from 'ol/layer'
import { XYZ } from 'ol/source'
const baseLayer = new Tile({
source: new XYZ({
url: `sourceUrl`,
}),
})
當然這個圖層可以不一定是貼片地圖,不過大多數業務上還是以這個為主的。
GIS 服務中獲取裁切範圍#
然後需要以裁切的面要素數據建立一個Vector
圖層,並綁定其 source 的addFeature
事件使基礎圖層可見範圍與clipLayer
一致
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import EsriJson from 'ol/format/EsriJSON';
const clipLayer = new VectorLayer({
source: new VectorSource({
url: `提供面要素服務地址`,
format: new EsriJson() // 這裡因為gis服務是EsriJson格式 如果是GeoJson或其他則使用對應的format
});
});
clipLayer.getSource().on('addfeature', ()=> {
baseLayer.setExtent(clipLayer.getSource().getExtent());
});
如此基礎圖層就只會加載切片範圍裡的貼片了,接著就是需要對邊界進行裁切。
這裡需要用到圖層的postrender
事件,這是在貼圖渲染完成後但未加載淡入的事件,然後通過對此時 canvas 的globalCompositeOperation
的設置就可以得到裁切的效果
baseLayer.on('postrender', (evt) => {
if (!evt.context || !('save' in evt.context)) return
const vecCtx = getVectorContext(evt)
evt.context.globalCompositeOperation = 'destination-in'
clipLayer.getSource().forEachFeature((feature) => {
vecCtx.drawFeature(feature, style)
})
evt.context.globalCompositeOperation = 'source-over'
})
當globalCompositeOperation
設置為destination-in
,canvas 會僅繪製與畫布內容重合的內容,其他會保持透明,同時我們將getVectorContext(evt)
即baseLayer
重新繪製要素就能夠獲得被裁切後的貼片地圖了。
靜態內容中獲取裁切範圍#
文件中獲取就是把VectorLayer
構造參數中的 url 換成 features 然後用 format 從文件中讀取 features 就行了
const clipLayer = new VectorLayer({
source: new VectorSource({
features: [new EsriJson().readFeatures(require('路徑'))],//format 同服務
});
});