
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
基于 WebGL2 的高性能地图渲染引擎,支持 EPSG:3857 投影。
bun add mapjar
# 或
npm install mapjar
import { MapEngine, TileLayer, VectorLayer } from 'mapjar';
// 创建地图引擎(自动初始化并开始渲染)
const engine = new MapEngine('#map', {
center: [116.4074, 39.9042], // 北京 [经度, 纬度]
zoom: 10,
rotation: 0,
enableRotation: true // 启用右键旋转(默认 true)
});
// 添加瓦片图层
const tileLayer = new TileLayer(
'osm',
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{
tileScale: 1.0, // 瓦片缩放比例(0.5 - 3.0)
wrapX: true, // 启用水平跨世界渲染
fadeInDuration: 200 // 瓦片淡入动画时长(毫秒)
}
);
engine.addLayer(tileLayer);
// 添加矢量图层
const vectorLayer = new VectorLayer('vector', {
fillColor: [0.2, 0.6, 1.0, 0.4],
strokeColor: [0.0, 0.4, 0.8, 1.0],
strokeWidth: 2.0,
pointSize: 10.0
});
engine.addLayer(vectorLayer);
// 添加点要素
vectorLayer.addFeature({
type: 'point',
coordinates: [116.4074, 39.9042],
properties: { name: '北京' }
});
// 监听点击事件
engine.on('click', (event) => {
console.log('点击位置:', event.lon, event.lat);
});
// 使用 flyTo 飞到上海
setTimeout(() => {
engine.flyTo(121.4737, 31.2304, 10, { duration: 2000 });
}, 2000);
// 可选:手动控制渲染循环
// engine.stop(); // 暂停渲染
// engine.start(); // 恢复渲染
使用 EPSG:3857 (Web Mercator) 投影:
import { WebMercatorProjection } from 'mapjar';
// 经纬度转 Web Mercator 坐标(米)
const pos = WebMercatorProjection.lonLatToMeters(116.4074, 39.9042);
// Web Mercator 坐标转经纬度
const lonLat = WebMercatorProjection.metersToLonLat(pos.x, pos.y);
// 获取瓦片坐标
const tile = WebMercatorProjection.getTileCoord(116.4074, 39.9042, 10);
const camera = engine.getCamera();
// 设置中心点(经纬度)
camera.setCenterLonLat(116.4074, 39.9042);
// 设置缩放级别(0-22)
camera.setZoom(10);
// 平移(像素)
camera.pan(100, 100);
// 缩放到指定点
camera.zoomTo(1, screenPos);
// 飞行到指定位置(带平滑动画)
engine.flyTo(116.4074, 39.9042, 10, { duration: 1500 });
// 适配到指定边界(带平滑动画)
engine.fitBounds(
{
minLon: 73.5,
minLat: 18.2,
maxLon: 135.0,
maxLat: 53.5
},
{ duration: 2000, padding: 50 }
);
const tileLayer = new TileLayer(
'osm',
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{
tileScale: 1.0, // 瓦片缩放比例(0.5 - 3.0,默认 1.0)
wrapX: true, // 启用水平跨世界渲染(默认 true)
fadeInDuration: 200 // 瓦片淡入动画时长(毫秒,默认 200)
}
);
engine.addLayer(tileLayer);
// 动态调整瓦片缩放比例
tileLayer.setTileScale(1.5); // 放大 50%,让文字更易读
// 特性:
// - Web Worker 并发加载(自动启用)
// - LRU 缓存(最多 500 个瓦片)
// - 智能取消机制(视口外的瓦片自动取消加载)
// - 淡入动画(平滑加载效果)
// - 跨世界渲染(水平无限循环)
import { ImageLayer } from 'mapjar';
// 创建图像图层(用于历史地图叠加、卫星影像等)
const imageLayer = new ImageLayer('custom-overlay', {
url: 'https://example.com/historical-map.png',
bounds: {
minLon: 116.2, // 左下角经度
minLat: 39.8, // 左下角纬度
maxLon: 116.6, // 右上角经度
maxLat: 40.1, // 右上角纬度
},
useMipmap: true, // 启用 Mipmap(默认 true)
});
imageLayer.setOpacity(0.7); // 半透明叠加
imageLayer.setZIndex(10); // 在底图之上
engine.addLayer(imageLayer);
// 异步加载图像
await imageLayer.loadFromURL();
// 或直接提供图像
const imageLayer2 = new ImageLayer('overlay2', {
image: imageBitmap, // HTMLImageElement 或 ImageBitmap
bounds: { minLon: 116.2, minLat: 39.8, maxLon: 116.6, maxLat: 40.1 }
});
// 特性:
// - 支持 URL 或直接提供图像
// - Mipmap 优化(多级纹理)
// - 各向异性过滤(提升清晰度)
// - 自动地理配准
const vectorLayer = new VectorLayer('vector', {
fillColor: [0.2, 0.6, 1.0, 0.4],
strokeColor: [0.0, 0.4, 0.8, 1.0],
strokeWidth: 2.0,
pointSize: 10.0,
// 文字样式
textField: 'name', // 显示 properties.name
textFont: '14px Arial',
textColor: [0, 0, 0, 1],
textHaloColor: [1, 1, 1, 1],
textHaloWidth: 2,
textOffset: [0, -15],
textAnchor: 'center'
});
// 添加点(渲染为圆形,带抗锯齿)
vectorLayer.addFeature({
type: 'point',
coordinates: [116.4074, 39.9042],
properties: { name: '北京' }
});
// 添加线
vectorLayer.addFeature({
type: 'line',
coordinates: [[116.4074, 39.9042], [121.4737, 31.2304]],
properties: { name: '路线' }
});
// 添加面(使用 Earcut 三角化,支持凹多边形和带洞多边形)
vectorLayer.addFeature({
type: 'polygon',
coordinates: [[[116, 39], [117, 39], [117, 40], [116, 40], [116, 39]]],
properties: { name: '区域' }
});
// 添加带洞的多边形
vectorLayer.addFeature({
type: 'polygon',
coordinates: [
[[116, 39], [117, 39], [117, 40], [116, 40], [116, 39]], // 外环
[[116.3, 39.3], [116.7, 39.3], [116.7, 39.7], [116.3, 39.7], [116.3, 39.3]] // 洞
],
properties: { name: '带洞区域' }
});
// 空间查询
const nearbyFeatures = vectorLayer.queryNearby([116.4, 39.9], 10000); // 10km 范围内
const featuresInBounds = vectorLayer.queryBBox({ minX: 116, minY: 39, maxX: 117, maxY: 40 });
// 特性:
// - 圆形点渲染(片段着色器实现,带抗锯齿)
// - Earcut 多边形三角化(支持凹多边形和带洞多边形)
// - 文字标注(支持自定义字体、颜色、描边)
// - 空间查询(点查询、范围查询、最近邻查询)
// - 视锥剔除(自动剔除视口外的要素)
// - 批量渲染(减少 GPU 状态切换)
import { MapEngine, GeoJSONLayer, StyleFunction } from 'mapjar';
// 创建地图引擎
const engine = new MapEngine('#map', {
center: [105, 35],
zoom: 4,
});
// 从 URL 加载 GeoJSON
const geoJSONLayer = new GeoJSONLayer('geojson', {
url: 'https://example.com/data.geojson',
style: {
fillColor: [0.2, 0.6, 1.0, 0.4],
strokeColor: [0.0, 0.4, 0.8, 1.0],
strokeWidth: 2.0,
pointSize: 10.0,
textField: 'name',
textFont: '14px Arial'
}
});
engine.addLayer(geoJSONLayer);
await geoJSONLayer.loadFromURL();
// 或直接加载数据
const geoJSONLayer2 = new GeoJSONLayer('geojson2', {
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [116.4074, 39.9042]
},
properties: { name: '北京', population: 21540000 }
}
]
}
});
// 数据驱动样式
geoJSONLayer.setDataDrivenStyle({
fillColor: StyleFunction.createPropertyColorMap(
'type',
{
'residential': [0.8, 0.8, 0.6, 0.5],
'commercial': [1.0, 0.6, 0.6, 0.5],
'park': [0.4, 0.8, 0.4, 0.5],
},
[0.5, 0.5, 0.5, 0.5] // 默认颜色
)
});
// 支持所有 GeoJSON 几何类型
// Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection
import { MapEngine, WindLayer } from 'mapjar';
// 创建地图引擎
const engine = new MapEngine('#map', {
center: [105, 35],
zoom: 4,
});
// 创建风场图层
const windLayer = new WindLayer('wind', {
particleCount: 5000, // 粒子数量(默认 5000)
particleAge: 100, // 粒子生命周期(帧数,默认 100)
speedFactor: 0.5, // 速度因子(默认 0.5)
lineWidth: 1.0, // 线宽(默认 1.0)
fadeOpacity: 0.97, // 拖尾透明度(默认 0.97)
wrapX: true, // 启用跨世界渲染(默认 true)
colorRamp: [ // 颜色渐变(可选)
'#3288bd',
'#66c2a5',
'#abdda4',
'#e6f598',
'#fee08b',
'#fdae61',
'#f46d43',
'#d53e4f',
],
});
// 方式1:从图片加载风场数据(推荐)
// 图片格式:R 通道存储归一化的 U,G 通道存储归一化的 V,A 通道存储有效性
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = async () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
const uv = new Float32Array(canvas.width * canvas.height * 2);
const alpha = new Float32Array(canvas.width * canvas.height);
for (let i = 0; i < canvas.width * canvas.height; i++) {
const r = pixels[i * 4] / 255;
const g = pixels[i * 4 + 1] / 255;
const a = pixels[i * 4 + 3] / 255;
alpha[i] = a;
if (a === 0) {
uv[i * 2] = 0;
uv[i * 2 + 1] = 0;
} else {
uv[i * 2] = minU + r * (maxU - minU);
uv[i * 2 + 1] = minV + g * (maxV - minV);
}
}
windLayer.setData({
uv,
alpha,
width: canvas.width,
height: canvas.height,
minU: -12.35,
maxU: 22.81,
minV: -22.71,
maxV: 14.65,
bounds: {
minLon: 55,
minLat: 1,
maxLon: 155,
maxLat: 57,
},
});
};
img.src = 'wind-data.png';
// 方式2:直接使用数值数组
const windData = {
uv: new Float32Array([...]), // UV 数据数组 [u0, v0, u1, v1, ...]
width: 100, // 数据宽度
height: 50, // 数据高度
minU: -10, // U 分量最小值
maxU: 10, // U 分量最大值
minV: -10, // V 分量最小值
maxV: 10, // V 分量最大值
alpha: new Float32Array([...]), // 可选:透明度数组
bounds: { // 地理边界(可选)
minLon: 73.5,
minLat: 18.0,
maxLon: 135.0,
maxLat: 53.5,
},
};
windLayer.setData(windData);
engine.addLayer(windLayer);
// 特性:
// - 粒子系统渲染,GPU 加速
// - 支持从图片加载数据(R/G 通道存储 U/V,A 通道存储有效性)
// - 根据风速自动映射颜色
// - 平滑的拖尾效果
// - 缩放自适应(粒子大小和速度)
// - 跨世界渲染支持
// - 交互时自动暂停渲染
import { MapEngine, HeatmapLayer } from 'mapjar';
// 创建地图引擎
const engine = new MapEngine('#map', {
center: [105, 35],
zoom: 4,
});
// 创建热力图层
const heatmapLayer = new HeatmapLayer('temperature', {
colorRamp: [
{ value: 0.0, color: '#313695' }, // 最冷
{ value: 0.5, color: '#ffffbf' }, // 中间
{ value: 1.0, color: '#a50026' }, // 最热
],
wrapX: true, // 启用跨世界渲染(默认 true)
});
// 方式1:直接使用图片(推荐,更高效)
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = async () => {
const bitmap = await createImageBitmap(img);
heatmapLayer.setData({
image: bitmap,
bounds: {
minLon: 55,
minLat: 1,
maxLon: 155,
maxLat: 57,
},
});
engine.addLayer(heatmapLayer);
};
img.src = 'temperature.png';
// 方式2:使用数值数组(向后兼容)
heatmapLayer.setData({
values: new Float32Array([...]), // 温度值数组
width: 100,
height: 50,
min: -10,
max: 40,
alpha: new Float32Array([...]), // 可选:透明度数组
bounds: {
minLon: 55,
minLat: 1,
maxLon: 155,
maxLat: 57,
},
});
// 动态切换颜色方案
heatmapLayer.setColorRamp([
{ value: 0.0, color: '#0000FF' },
{ value: 0.5, color: '#00FF00' },
{ value: 1.0, color: '#FF0000' },
]);
// 特性:
// - 直接使用图片作为纹理,GPU 加速
// - 支持自定义颜色渐变(ColorStop 格式)
// - 自动 Y 轴翻转以匹配地理坐标系
// - 支持透明度通道(A 通道)
// - 跨世界渲染支持
// - 动态切换颜色方案
颜色映射格式:
// 格式1:字符串数组(均匀分布)
colorRamp: ['#0000FF', '#00FF00', '#FF0000']
// 格式2:ColorStop 数组(精确控制,推荐)
colorRamp: [
{ value: 0.0, color: '#0000FF' }, // 0% 位置
{ value: 0.3, color: '#00FF00' }, // 30% 位置
{ value: 1.0, color: '#FF0000' }, // 100% 位置
]
应用场景:
import { OverlayLayer } from 'mapjar';
// 创建覆盖层图层(一个图层只包含一个覆盖层)
const overlayLayer = new OverlayLayer('overlay');
engine.addLayer(overlayLayer);
// 创建 HTML 元素
const element = document.createElement('div');
element.innerHTML = `
<div style="
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
font-family: Arial;
">
<h3 style="margin: 0 0 5px 0;">北京</h3>
<p style="margin: 0; color: #666;">中国首都</p>
</div>
`;
// 设置覆盖层
overlayLayer.setOverlay({
element: element,
position: {
lon: 116.4074,
lat: 39.9042,
offset: [0, -20], // 向上偏移 20px
anchor: [0.5, 1.0], // 底部中心锚点
},
properties: { name: '北京' },
});
// 更新位置
overlayLayer.updateOverlay({
position: {
lon: 116.5,
lat: 40.0,
}
});
// 更新元素
const newElement = document.createElement('div');
newElement.textContent = '新内容';
overlayLayer.updateOverlay({
element: newElement
});
// 更新多个参数
overlayLayer.updateOverlay({
element: newElement,
position: { lon: 116.5, lat: 40.0 },
visible: false
});
// 清空覆盖层
overlayLayer.clearOverlay();
// 特性:
// - 在地图上叠加单个 HTML 元素
// - 自动跟随地图平移和缩放
// - 支持自定义锚点和偏移
// - 支持完整的 CSS 样式和交互
// - 支持更新所有参数(element、position、visible、properties)
// - 统一的 DPI 管理,在高分辨率屏幕上位置准确
// - 适合复杂的标注、弹窗、自定义控件等
位置配置:
interface OverlayPosition {
lon: number; // 经度
lat: number; // 纬度
offset?: [number, number]; // 偏移 [x, y],默认 [0, 0]
anchor?: [number, number]; // 锚点 [x, y],默认 [0.5, 0.5](中心)
}
// 锚点示例:
// [0, 0] - 左上角
// [0.5, 0] - 顶部中心
// [1, 0] - 右上角
// [0, 0.5] - 左侧中心
// [0.5, 0.5] - 中心(默认)
// [1, 0.5] - 右侧中心
// [0, 1] - 左下角
// [0.5, 1] - 底部中心
// [1, 1] - 右下角
更新参数:
// 可以更新任意参数组合
overlayLayer.updateOverlay({
element?: HTMLElement, // 更新 HTML 元素
position?: OverlayPosition, // 更新位置
visible?: boolean, // 更新可见性
properties?: Record<string, unknown> // 更新属性
});
应用场景:
import { CanvasLayer } from 'mapjar';
// 创建自定义 Canvas 图层
class MyCanvasLayer extends CanvasLayer {
constructor(id: string) {
super(id, 512, 512); // 指定 Canvas 尺寸
}
// 实现自定义渲染逻辑
render(gl: WebGL2RenderingContext, viewMatrix: Float32Array): void {
if (!this.visible) return;
const ctx = this.getContext();
// 清空 Canvas
this.clear();
// 使用 Canvas 2D API 绘制
ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
ctx.fillRect(100, 100, 200, 200);
ctx.strokeStyle = 'blue';
ctx.lineWidth = 3;
ctx.strokeRect(150, 150, 100, 100);
// Canvas 内容会自动转换为 WebGL 纹理渲染
}
}
const canvasLayer = new MyCanvasLayer('custom');
engine.addLayer(canvasLayer);
// 特性:
// - 纯 Canvas 2D 渲染
// - 自动转换为 WebGL 纹理
// - 适合自定义绘制逻辑
// - 支持所有 Canvas 2D API
根据要素属性动态设置样式:
import { VectorLayer, StyleFunction } from 'mapjar';
const layer = new VectorLayer('vector');
// 基于属性的颜色映射
layer.setDataDrivenStyle({
fillColor: StyleFunction.createPropertyColorMap(
'type',
{
'residential': [0.8, 0.8, 0.6, 0.5], // 住宅区 - 米黄色
'commercial': [1.0, 0.6, 0.6, 0.5], // 商业区 - 粉红色
'park': [0.4, 0.8, 0.4, 0.5], // 公园 - 绿色
},
[0.5, 0.5, 0.5, 0.5] // 默认颜色
)
});
// 数值范围的颜色插值
layer.setDataDrivenStyle({
fillColor: StyleFunction.createNumericColorScale(
'population',
[
[0, [0.2, 0.4, 1.0, 0.5]], // 低密度 - 蓝色
[5000, [0.4, 0.8, 0.8, 0.5]], // 中密度 - 青色
[10000, [0.8, 0.8, 0.4, 0.5]], // 中高密度 - 黄色
[20000, [1.0, 0.4, 0.2, 0.5]], // 高密度 - 橙色
],
[0.5, 0.5, 0.5, 0.5]
)
});
// 自定义样式函数
layer.setDataDrivenStyle({
fillColor: (properties) => {
const value = properties.temperature as number;
if (value < 0) return [0.2, 0.4, 1.0, 0.6]; // 冷 - 蓝色
if (value < 20) return [0.4, 0.8, 0.4, 0.6]; // 温和 - 绿色
if (value < 30) return [1.0, 0.8, 0.2, 0.6]; // 温暖 - 黄色
return [1.0, 0.2, 0.2, 0.6]; // 热 - 红色
},
pointSize: (properties) => {
const importance = properties.importance as number || 1;
return importance * 2;
}
});
样式函数工具:
createPropertyColorMap - 基于分类属性的颜色映射createNumericColorScale - 基于数值范围的颜色插值createNumericSizeScale - 基于数值范围的大小插值createConditionalStyle - 基于条件的样式选择应用场景:
// 获取图层
const layer = engine.getLayer('osm');
// 设置可见性
layer?.setVisible(false);
// 设置透明度
layer?.setOpacity(0.5);
// 设置层级
layer?.setZIndex(10);
// 移除图层
engine.removeLayer('osm');
// 新的事件 API(推荐)
engine.on('click', (event) => {
console.log('点击:', event.lon, event.lat);
});
engine.on('mousemove', (event) => {
console.log('鼠标:', event.lon, event.lat);
});
// 一次性事件监听
engine.once('click', (event) => {
console.log('只触发一次');
});
// 移除事件监听
const removeListener = engine.on('click', handler);
removeListener(); // 调用返回的函数移除监听
// 或使用 off 方法
engine.off('click', handler);
// 移除所有监听器
engine.removeAllListeners('click');
engine.removeAllListeners(); // 移除所有事件的监听器
// 事件类型
type MapEventMap = {
click: MapClickEvent;
mousemove: MapMouseMoveEvent;
};
| 指标 | 主线程 | Worker | 提升 |
|---|---|---|---|
| 首屏加载 | 2.5s | 1.8s | 28% ↓ |
| 主线程阻塞 | 150ms | 20ms | 87% ↓ |
| 帧率下降 | 15 fps | 3 fps | 80% ↓ |
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 纹理上传次数 | 每帧 | 仅一次 | 99% ↓ |
| 渲染时间 | 8ms | 3ms | 62% ↓ |
| 视觉质量 | ⭐⭐ | ⭐⭐⭐⭐⭐ | 显著提升 |
tileScale,平衡清晰度和性能wrapX: true,区域地图禁用engine.stop() 停止渲染覆盖 97%+ 的浏览器
平滑飞行到指定位置和缩放级别。
参数:
lon (number): 目标经度lat (number): 目标纬度zoom (number, 可选): 目标缩放级别,默认保持当前缩放options (object, 可选):
duration (number): 动画时长(毫秒),默认自动计算maxDuration (number): 最大动画时长(毫秒),默认 3000示例:
// 飞到北京,缩放到 10 级
engine.flyTo(116.4074, 39.9042, 10);
// 自定义动画时长
engine.flyTo(121.4737, 31.2304, 12, { duration: 2000 });
// 只改变位置,保持当前缩放
engine.flyTo(113.2644, 23.1291);
自动适配到指定边界,并平滑过渡。
参数:
bounds (object): 边界对象
minLon (number): 最小经度minLat (number): 最小纬度maxLon (number): 最大经度maxLat (number): 最大纬度options (object, 可选):
duration (number): 动画时长(毫秒),默认自动计算maxDuration (number): 最大动画时长(毫秒),默认 3000padding (number): 边界填充(像素),默认 50示例:
// 适配中国边界
engine.fitBounds({
minLon: 73.5,
minLat: 18.2,
maxLon: 135.0,
maxLat: 53.5
});
// 自定义填充和动画时长
engine.fitBounds(
{
minLon: 110,
minLat: 30,
maxLon: 120,
maxLat: 40
},
{ padding: 100, duration: 1500 }
);
相机级别的 flyTo 方法,与 MapEngine.flyTo 相同。
相机级别的 fitBounds 方法,与 MapEngine.fitBounds 相同。
mapjar/
├── lib/ # 库源代码
│ ├── animation/ # 动画系统
│ │ ├── Easing.ts # 缓动函数
│ │ ├── FlyToAnimation.ts # 飞行动画
│ │ └── ZoomAnimation.ts # 缩放动画
│ ├── core/ # 核心模块
│ │ ├── Camera.ts # 相机系统
│ │ └── WebGL2Renderer.ts # WebGL2 渲染器
│ ├── layer/ # 图层系统
│ │ ├── Layer.ts # 图层基类
│ │ ├── RasterLayer.ts # 栅格图层基类
│ │ ├── TileLayer.ts # 瓦片图层
│ │ ├── ImageLayer.ts # 图像图层
│ │ ├── VectorLayer.ts # 矢量图层
│ │ ├── GeoJSONLayer.ts # GeoJSON 图层
│ │ ├── WindLayer.ts # 风场动画图层
│ │ ├── HeatmapLayer.ts # 热力图层
│ │ ├── OverlayLayer.ts # 覆盖层图层
│ │ └── CanvasLayer.ts # Canvas 图层
│ ├── math/ # 数学工具
│ │ ├── Vec2.ts # 二维向量
│ │ └── Projection.ts # 投影系统
│ ├── spatial/ # 空间查询
│ │ └── SpatialQuery.ts # 空间查询工具
│ ├── style/ # 样式系统
│ │ └── StyleFunction.ts # 数据驱动样式
│ ├── utils/ # 工具类
│ │ ├── BatchRenderer.ts # 批量渲染器
│ │ ├── EventEmitter.ts # 事件发射器
│ │ ├── FrustumCulling.ts # 视锥剔除
│ │ ├── Loader.ts # 资源加载器
│ │ ├── ResourceManager.ts # 资源管理器
│ │ ├── TextRenderer.ts # 文字渲染器
│ │ └── WebGLUtils.ts # WebGL 工具函数
│ ├── workers/ # Web Workers
│ │ ├── TileLoader.worker.ts # 瓦片加载 Worker
│ │ └── TileLoader.worker.factory.ts # Worker 工厂
│ ├── MapEngine.ts # 地图引擎主类
│ └── main.ts # 导出入口
├── examples/ # 示例应用
│ ├── views/ # 示例页面
│ │ ├── Home.vue # 首页
│ │ ├── TileLayerExample.vue # 瓦片图层示例
│ │ ├── VectorLayerExample.vue # 矢量图层示例
│ │ ├── GeoJSONLayerExample.vue # GeoJSON 图层示例
│ │ ├── ImageLayerExample.vue # 图像图层示例
│ │ ├── WindLayerExample.vue # 风场图层示例
│ │ ├── WindLayerExample2.vue # 风场图层示例 2
│ │ ├── HeatmapLayerExample.vue # 热力图层示例
│ │ ├── OverlayLayerExample.vue # 覆盖层图层示例
│ │ └── CombinedExample.vue # 综合示例
│ ├── router/ # 路由配置
│ ├── components/ # 公共组件
│ ├── App.vue # 应用根组件
│ └── main.ts # 应用入口
└── index.d.ts # TypeScript 类型声明
Q: 瓦片加载失败?
A: 检查瓦片 URL 是否正确,是否需要 API Key,是否有跨域问题(CORS)。
Q: 矢量要素不显示?
A: 确保坐标在可见范围内,检查图层的 zIndex 是否被其他图层遮挡,检查样式的透明度是否为 0。
Q: 性能问题?
A: 减少矢量要素数量(建议 < 10000),使用瓦片图层代替大量矢量数据,调整 tileScale 平衡清晰度和性能,启用视锥剔除。
Q: 瓦片显示模糊?
A: 瓦片已自动启用 Mipmap 和各向异性过滤。如果文字太小,可以增加 tileScale 参数(1.0 - 3.0)。
Q: 地图边界有缝隙?
A: 确保瓦片服务器支持跨越 180° 经线的瓦片,或禁用 wrapX 选项。
Q: 如何添加自定义瓦片源?
A: 只需提供符合 {z}/{x}/{y} 格式的 URL 模板即可。
Q: 风场/热力图数据如何准备?
A: 推荐使用图片格式:风场数据用 R/G 通道存储 U/V 分量,A 通道存储有效性;热力图直接使用灰度图或彩色图。
Q: 移动端显示太小?
A: 引擎已自动适配高 DPI 屏幕(通过 Camera.getResolution() 的 DPI 缩放),无需额外配置。
Q: 如何自定义图层?
A: 继承 Layer、RasterLayer 或 CanvasLayer 基类,实现 render() 方法即可。
MIT
欢迎提交 Issue 和 Pull Request!
FAQs
基于 WebGL2 的高性能地图渲染引擎,支持 EPSG:3857 投影。
The npm package mapjar receives a total of 12 weekly downloads. As such, mapjar popularity was classified as not popular.
We found that mapjar demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.