เอกสาร / มุมมองแผนที่ / เปลี่ยนสไตล์แผนที่

เปลี่ยนสไตล์แผนที่

แพ็กเกจที่ใช้ปรับแต่งเวกเตอร์สไตล์ของแผนที่ เช่น "ทำให้ถนนเป็นสีขาว" "ซ่อนป้ายชื่อ" "ทำให้พื้นที่น้ำมืดลง" โดยประกาศเป็นกฎเชิงประกาศ (declarative) กฎชุดเดียวกันใช้ได้ทันทีบน Android และ iOS ด้วย

REACT
@mapconductor/js-vectorstyle
PROP
mapStyle
ESM / CJS
รองรับเฉพาะ ESM

01 · ภาพรวม

กฎคือลำดับของคู่ "เลเยอร์ใด (ตัวเลือก selector)" และ "ทำอะไร (แพตช์ patch)" ตัวเลือกสามารถเขียนตามบทบาท (ถนน พื้นที่น้ำ ป้ายชื่อ…) แทนที่จะใช้ ID ของเลเยอร์ สคีมาของ OpenMapTiles, Shortbread และ Mapbox Streets ถูกแยกแยะโดยอัตโนมัติ จึงไม่ต้องค้นหา ID เลเยอร์ที่ต่างกันของแต่ละสไตล์

ส่งค่าที่ createVectorStyle() คืนไปเป็น mapStyle ให้กับ *MapView ใดก็ได้ ใน React ชื่อเป็น mapStyle เพราะ style เป็น CSS ของตัวองค์ประกอบเอง (บน Android และ iOS ใช้ style)

แผนที่
ทำงานอย่างไร
สิ่งที่ต้องมี
MapLibre / Mapbox / MapTiler
แผนที่เป็นผู้อ่านสไตล์เอง แม้เปลี่ยนกฎก็ใส่เฉพาะส่วนต่าง ไม่ต้องโหลดไทล์ใหม่
แผนที่ในแพ็กเกจนี้เท่านั้น
แผนที่อื่น ๆ (Google Maps, Leaflet, OpenLayers, ArcGIS, Cesium, HERE, MapKit, deck.gl ฯลฯ)
วาดสไตล์ลงบนแรสเตอร์ไทล์แล้วซ้อนทับ
ต้องระบุ VectorTileRasteriser ให้กับ rasteriser

หากส่งไปยังแผนที่ที่ไม่รองรับเวกเตอร์โดยไม่ส่งแรสเตอร์ไรเซอร์ ระบบจะไม่วาดอะไรเงียบ ๆ แต่จะแสดงข้อวินิจฉัยว่า "สไตล์นี้ใช้ไม่ได้"

02 · การติดตั้ง

terminalnpm
npm install @mapconductor/js-sdk-core @mapconductor/js-vectorstyle

หากต้องการใช้กับแผนที่ที่ไม่รองรับเวกเตอร์ ให้ติดตั้ง react-vectortile ที่มีแรสเตอร์ไรเซอร์ด้วย (ต้องเตรียม Service Worker ของ เลเยอร์เวกเตอร์ไทล์ ด้วย)

terminalnpm
npm install @mapconductor/react-vectortile

รองรับเฉพาะ ESM คอมไพเลอร์กฎเป็น wasm (ประมาณ 190 KB) และโหลดแบบสัมพัทธ์จาก import.meta.url หากใช้ Vite จะทำงานได้ทันที หากต้องการเสิร์ฟจากตำแหน่งอื่น ให้ระบุด้วย wasmUrl

03 · วิธีใช้พื้นฐาน

หากไม่ระบุ document จะเป็นการปรับสไตล์ที่แผนที่กำลังแสดงอยู่ ('currentDesign') ตัวอย่างด้านล่างเปลี่ยนดีไซน์เริ่มต้นของ MapLibre ให้เป็นแผนที่ขาวดำที่มองเห็นเฉพาะถนน

RoadsOnlyMap.tsxTypeScript · React
import { useMemo } from 'react';
import { buildStyleRules, createVectorStyle, LayerRole } from '@mapconductor/js-vectorstyle';
import { MapLibreMapView2D, useMapLibreViewState } from '@mapconductor/react-for-maplibre';

export function RoadsOnlyMap() {
  const state = useMapLibreViewState();
  const mapStyle = useMemo(
    () => createVectorStyle({
      rules: buildStyleRules((r) => {
        r.all({ color: '#000000' });
        r.role(LayerRole.label, { visible: false });
        r.role(LayerRole.roadCasing, { color: '#303030' });
        r.role(LayerRole.road, { color: '#ffffff', widthScale: 1.4 });
      }),
    }),
    [],
  );

  return (
    <MapLibreMapView2D
      state={state}
      mapStyle={mapStyle}
      onStyleDiagnostics={(messages) => console.warn(messages)}
    />
  );
}

เมื่อใช้สไตล์ของตัวเอง ให้ส่ง { url } หรือ { text } ไปที่ document

04 · วิธีเขียนกฎ

กฎจะถูกนำไปใช้ตามลำดับจากบนลงล่าง เหมือน CSS คือตัวที่อยู่ท้ายจะชนะในแต่ละรายการ วิธีพื้นฐานคือกำหนดทั้งหมดด้วย all ก่อน แล้วค่อยเขียนทับตามบทบาท

TypeScriptTypeScript
buildStyleRules((r) => {
  r.all({ colorFilter: { darken: 0.55 } });
  r.role(LayerRole.water, { color: 'rgb(10,28,43)' });
  r.role(LayerRole.road, { color: '#d0d0d0' });
  r.layerId('poi-*', { visible: false });
  r.kind('symbol', { opacity: 0.8 });
});
ตัวเลือก (selector)
ตัวสร้าง (builder)
คำอธิบาย
all
r.all(patch)
ทุกเลเยอร์
{ role }
r.role(LayerRole.road, patch)
บทบาท: background, water, waterway, land, landuse, park, building, road, road-casing, rail, transit, boundary, aeroway, label, poi
{ layerId }
r.layerId('poi-*', patch)
ID ของเลเยอร์ ใช้ * และ ? ได้
{ sourceLayer }
r.sourceLayer('water', patch)
ชื่อ source-layer ของไทล์
{ kind }
r.kind('symbol', patch)
ชนิดของเลเยอร์: background, fill, line, circle, symbol, other
anyOf / allOf / not
r.where(selector, patch)
การรวมกัน
แพตช์
ชนิด
คำอธิบาย
visible
boolean
การแสดง/ซ่อน
color
string
สีที่เหมาะกับชนิดของเลเยอร์ (พื้นที่, เส้น, ตัวอักษรและไอคอน, พื้นหลัง)
colorFilter
{ desaturate } | { darken } | { lighten } | { invertLightness } | { mix }
การแปลงสีจากสีเดิม
opacity
0–1
ความทึบ
widthScale
number
ตัวคูณของความกว้างเส้นและรัศมีวงกลม
filter
นิพจน์ของ MapLibre
แทนที่ฟิลเตอร์ของเลเยอร์
minZoom / maxZoom
number
ช่วงซูมที่แสดงผล
properties
Record<string, unknown>
ระบุพร็อพเพอร์ตี้ของสไตล์สเปกโดยตรงด้วยชื่อ จะถูกนำไปใช้เป็นลำดับสุดท้าย

เก็บกฎในรูป JSON

กฎยังเป็นเอกสาร JSON ด้วย เมื่อต้องส่งจากเซิร์ฟเวอร์หรือบันทึกข้ามเวอร์ชันของแอป ให้เก็บในรูปแบบนี้แล้วอ่านด้วย parseStyleRules() เอกสารเดียวกันนี้อ่านได้ทั้งบน Android และ iOS

night.rules.jsonJSON
{
  "schemaVersion": 1,
  "rules": [
    { "selector": "all", "patch": { "colorFilter": { "darken": 0.55 } } },
    { "selector": { "role": "water" }, "patch": { "color": "rgb(10,28,43)" } }
  ]
}
TypeScriptTypeScript
import { createVectorStyle, parseStyleRules } from '@mapconductor/js-vectorstyle';

const rules = parseStyleRules(await (await fetch('/night.rules.json')).text());
const mapStyle = createVectorStyle({ rules });

05 · ใช้กับแผนที่ที่ไม่รองรับเวกเตอร์

เมื่อส่ง VectorTileRasteriser ไปที่ rasteriser จะวาดสไตล์ลงบนแรสเตอร์ไทล์แล้วซ้อนทับ ให้ซ่อนแผนที่ของตัวโปรไวเดอร์เองโดยตั้งดีไซน์เป็น None

TypeScriptTypeScript · React
import { useMemo } from 'react';
import { createVectorStyle } from '@mapconductor/js-vectorstyle';
import { VectorTileRasteriser } from '@mapconductor/react-vectortile';
import { GoogleMapDesign, GoogleMapView2D, useGoogleMapViewState } from '@mapconductor/react-for-googlemaps';

const state = useGoogleMapViewState({
  apiKey: import.meta.env.VITE_GOOGLE_MAPS_API_KEY,
  mapDesignType: GoogleMapDesign.None,
});
const rasteriser = useMemo(() => new VectorTileRasteriser(), []);
const mapStyle = useMemo(
  () => createVectorStyle({
    document: { url: 'https://tiles.versatiles.org/assets/styles/colorful/style.json' },
    rules,
    rasteriser,
  }),
  [rules, rasteriser],
);

<GoogleMapView2D state={state} mapStyle={mapStyle} style={{ height: 480 }} />
ข้อควรระวัง
ตัวแรสเตอร์ไรเซอร์เปรียบเทียบด้วยความเป็นอ็อบเจกต์เดียวกัน หากสร้างด้วย new ทุกครั้งที่วาด จะถือเป็นสไตล์อื่นและแผนที่จะถูกสร้างใหม่ทุกครั้ง ควรคงอ็อบเจกต์เดียวไว้ด้วย useMemo หรือวิธีที่คล้ายกัน

06 · ตัวเลือกหลัก

ตัวเลือก
ค่าเริ่มต้น
คำอธิบาย
document
'currentDesign'
สไตล์ที่จะปรับ { url, headers? }, { text }, 'currentDesign'
rules
—
กฎที่จะใช้ ถ้าเป็น NO_STYLE_RULES ก็จะคงสภาพเดิม
rasteriser
—
สำหรับวาดบนแผนที่ที่ไม่รองรับเวกเตอร์
onUnsupported
'report'
เมื่อแผนที่ไม่ยอมรับผลต่าง ถ้าเป็น 'reload' จะส่งเอกสารที่ปรับแล้วทั้งชุด (ถูกต้องแต่หนัก)
onDiagnostics
—
จำนวนเลเยอร์ที่ถูกจับคู่ กฎที่ไม่ถูกจับคู่เลย และผลการปรับที่แผนที่ปฏิเสธ
wasmUrl
—
ตำแหน่ง wasm ของคอมไพเลอร์

สามารถรับข้อมูลวินิจฉัยได้ทาง onStyleDiagnostics ของ *MapView ด้วย หากละไว้จะแสดงในคอนโซล "กฎที่ไม่ถูกจับคู่เลย" นั้นจากหน้าตาภายนอกแยกให้ออกว่า "เกือบจะทำงาน" เท่านั้น จึงแนะนำให้แสดงไว้ในที่ที่มองเห็นระหว่างการพัฒนา

หน้าที่เกี่ยวข้อง