Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MapLibre 4 custom modifications #3

Merged
merged 13 commits into from
Oct 3, 2024
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 4.7.1-beta.1

- Added custom features that were not part of the main branch
- latitude bounds
- support for texture formats other than gl.RGBA used by raster tile sources
- exports for Event, Tile, SourceCache, and OverscaledTileID

# === LeafletGL follows ===

## main

### ✨ Features and improvements
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "maplibre-gl",
"name": "@windycom/maplibre-gl",
"description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library",
"version": "5.0.0-pre.1",
"main": "dist/maplibre-gl.js",
Expand Down Expand Up @@ -189,4 +189,4 @@
"npm": ">=8.1.0",
"node": ">=16.14.0"
}
}
}
2 changes: 2 additions & 0 deletions src/geo/lng_lat_bounds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export type LngLatBoundsLike = LngLatBounds | [LngLatLike, LngLatLike] | [number
* A `LngLatBounds` object represents a geographical bounding box,
* defined by its southwest and northeast points in longitude and latitude.
*
* If the longitude bounds difference is over 360°, no longitude bound is applied.
*
* If no arguments are provided to the constructor, a `null` bounding box is created.
*
* Note that any Mapbox GL method that accepts a `LngLatBounds` object as an argument or option
Expand Down
6 changes: 4 additions & 2 deletions src/geo/projection/mercator_transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,9 @@ export class MercatorTransform implements ITransform {
if (shouldZoomIn) scaleY = screenHeight / (maxY - minY);
}

if (lngRange) {
const lngUnlimited = !lngRange || Math.abs(lngRange[0] - lngRange[1]) >= 1e6;

if (lngRange && !lngUnlimited) {
minX = wrap(
mercatorXfromLng(lngRange[0]) * worldSize,
0,
Expand Down Expand Up @@ -488,7 +490,7 @@ export class MercatorTransform implements ITransform {
if (originalY + h2 > maxY) modifiedY = maxY - h2;
}

if (lngRange) {
if (lngRange && !lngUnlimited) {
const centerX = (minX + maxX) / 2;
let wrappedX = originalX;
if (this._helper._renderWorldCopies) {
Expand Down
2 changes: 1 addition & 1 deletion src/geo/transform_helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ export class TransformHelper implements ITransformGetters {
setMaxBounds(bounds?: LngLatBounds | null): void {
if (bounds) {
this._lngRange = [bounds.getWest(), bounds.getEast()];
this._latRange = [bounds.getSouth(), bounds.getNorth()];
this._latRange = [Math.max(bounds.getSouth(), -MAX_VALID_LATITUDE), Math.min(bounds.getNorth(), MAX_VALID_LATITUDE)];
this._constrain();
} else {
this._lngRange = null;
Expand Down
11 changes: 9 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import packageJSON from '../package.json' with {type: 'json'};
import packageJSON from '../package.json' with { type: 'json' };
import {Map} from './ui/map';
import {NavigationControl} from './ui/control/navigation_control';
import {GeolocateControl} from './ui/control/geolocate_control';
Expand All @@ -14,7 +14,7 @@ import {LngLat, LngLatLike} from './geo/lng_lat';
import {LngLatBounds, LngLatBoundsLike} from './geo/lng_lat_bounds';
import Point from '@mapbox/point-geometry';
import {MercatorCoordinate} from './geo/mercator_coordinate';
import {Evented} from './util/evented';
import {Evented, Event} from './util/evented';
import {config} from './util/config';
import {rtlMainThreadPluginFactory} from './source/rtl_text_plugin_main_thread';
import {WorkerPool} from './util/worker_pool';
Expand All @@ -27,6 +27,8 @@ import {RasterDEMTileSource} from './source/raster_dem_tile_source';
import {RasterTileSource} from './source/raster_tile_source';
import {VectorTileSource} from './source/vector_tile_source';
import {VideoSource} from './source/video_source';
import {OverscaledTileID} from './source/tile_id';
import {Tile} from './source/tile';
import {Source, addSourceType} from './source/source';
import {addProtocol, removeProtocol} from './source/protocol_crud';
import {getGlobalDispatcher} from './util/dispatcher';
Expand All @@ -47,6 +49,7 @@ import {KeyboardHandler} from './ui/handler/keyboard';
import {TwoFingersTouchPitchHandler, TwoFingersTouchRotateHandler, TwoFingersTouchZoomHandler} from './ui/handler/two_fingers_touch';
import {MessageType} from './util/actor_messages';
import {createTileMesh} from './util/create_tile_mesh';
import {SourceCache} from './source/source_cache';
const version = packageJSON.version;

export type * from '@maplibre/maplibre-gl-style-spec';
Expand Down Expand Up @@ -188,6 +191,9 @@ export {
LngLatBounds,
Point,
MercatorCoordinate,
SourceCache,
Event,
Tile,
Evented,
AJAXError,
config,
Expand All @@ -213,6 +219,7 @@ export {
MapWheelEvent,
MapTouchEvent,
MapMouseEvent,
OverscaledTileID,
type IControl,
type CustomLayerInterface,
type CanvasSourceSpecification,
Expand Down
54 changes: 46 additions & 8 deletions src/render/texture.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type {Context} from '../gl/context';
import type {RGBAImage, AlphaImage} from '../util/image';
import {isImageBitmap} from '../util/util';
import {extend, isImageBitmap} from '../util/util';

export type TextureFormat = WebGLRenderingContextBase['RGBA'] | WebGLRenderingContextBase['ALPHA'];
export type TextureFormatWebGL2 = WebGL2RenderingContextBase['RG8'] | WebGL2RenderingContextBase['R8']
export type TextureFormat = WebGLRenderingContextBase['RGBA'] |WebGLRenderingContextBase['RGB'] | WebGLRenderingContextBase['ALPHA'] | WebGLRenderingContextBase['LUMINANCE'] | TextureFormatWebGL2;
export type TextureFilter = WebGLRenderingContextBase['LINEAR'] | WebGLRenderingContextBase['LINEAR_MIPMAP_NEAREST'] | WebGLRenderingContextBase['NEAREST'];
export type TextureWrap = WebGLRenderingContextBase['REPEAT'] | WebGLRenderingContextBase['CLAMP_TO_EDGE'] | WebGLRenderingContextBase['MIRRORED_REPEAT'];

Expand Down Expand Up @@ -35,12 +36,18 @@
this.context = context;
this.format = format;
this.texture = context.gl.createTexture();
this.update(image, options);

// Pass format to the update method to enforce its usage
this.update(image, options ? extend(options, {format}) : {format});
}

/**
* Updates texture content, can also change texture format if necessary
*/
update(image: TextureImage, options?: {
premultiply?: boolean;
useMipmap?: boolean;
format?:TextureFormat;
} | null, position?: {
x: number;
y: number;
Expand All @@ -51,27 +58,36 @@
const {gl} = context;

this.useMipmap = Boolean(options && options.useMipmap);

// Use default maplibre format gl.RGBA to remain compatible with all users of the Texture class
const newFormat = options?.format ?? gl.RGBA;
const formatChanged = this.format !== newFormat;
this.format = newFormat;

gl.bindTexture(gl.TEXTURE_2D, this.texture);

context.pixelStoreUnpackFlipY.set(false);
context.pixelStoreUnpack.set(1);
context.pixelStoreUnpackPremultiplyAlpha.set(this.format === gl.RGBA && (!options || options.premultiply !== false));

if (resize) {
// Since internal-format and format can be represented by different values (e.g. gl.RG8 vs gl.RG) in WebGL2, we need to preform conversion
const format = this.textureFormatFromInternalFormat(this.format);

if (resize || formatChanged) {
this.size = [width, height];

if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData || isImageBitmap(image)) {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.format, gl.UNSIGNED_BYTE, image);
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, format, gl.UNSIGNED_BYTE, image);
} else {
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, this.format, gl.UNSIGNED_BYTE, (image as DataTextureImage).data);
gl.texImage2D(gl.TEXTURE_2D, 0, this.format, width, height, 0, format, gl.UNSIGNED_BYTE, (image as DataTextureImage).data);
}

} else {
const {x, y} = position || {x: 0, y: 0};
if (image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof HTMLVideoElement || image instanceof ImageData || isImageBitmap(image)) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, format, gl.UNSIGNED_BYTE, image);
} else {
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, (image as DataTextureImage).data);
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, format, gl.UNSIGNED_BYTE, (image as DataTextureImage).data);
}
}

Expand Down Expand Up @@ -111,4 +127,26 @@
gl.deleteTexture(this.texture);
this.texture = null;
}

/**
* Method for accessing texture format by its internal format for cases, when these two are not the same
* - specifically for special WebGL2 formats
*/
textureFormatFromInternalFormat(internalFormat: TextureFormat) {
let format: GLenum = internalFormat;

if (!WebGL2RenderingContext) {

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

AttributionControl › shows attributions for sources that are used for terrain

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getTerrainData (src/render/terrain.ts:216:39) at Terrain.getMinMaxElevation (src/render/terrain.ts:446:27) at Terrain.getMinTileElevationForLngLatZoom (src/render/terrain.ts:434:21) at Map.setTerrain (src/ui/map.ts:2029:71) at Object.<anonymous> (src/ui/control/attribution_control.test.ts:329:13)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

Terrain › pointCoordinate should not return null

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getFramebuffer (src/render/terrain.ts:279:38) at Terrain.pointCoordinate (src/render/terrain.ts:336:42) at Object.<anonymous> (src/render/terrain.test.ts:62:36)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

Terrain › pointCoordinate should respect painter.pixelRatio

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getFramebuffer (src/render/terrain.ts:279:38) at Terrain.pointCoordinate (src/render/terrain.ts:336:42) at Object.<anonymous> (src/render/terrain.test.ts:132:38)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

Terrain › Calculate tile minimum and maximum elevation

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getTerrainData (src/render/terrain.ts:216:39) at Terrain.getMinMaxElevation (src/render/terrain.ts:446:27) at Object.<anonymous> (src/render/terrain.test.ts:171:54)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

Terrain › Return null elevation values when no tile

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getTerrainData (src/render/terrain.ts:216:39) at Terrain.getMinMaxElevation (src/render/terrain.ts:446:27) at Object.<anonymous> (src/render/terrain.test.ts:196:38)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

Terrain › Return null elevation values when no DEM

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at Terrain.getTerrainData (src/render/terrain.ts:216:39) at Terrain.getMinMaxElevation (src/render/terrain.ts:446:27) at Object.<anonymous> (src/render/terrain.test.ts:224:37)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

render to texture › should render text after a line by not adding the text to the stack

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at RenderPool._createObject (src/gl/render_pool.ts:43:25) at RenderPool.getOrCreateFreeObject (src/gl/render_pool.ts:76:26) at RenderToTexture.renderLayer (src/render/render_to_texture.ts:170:39) at Object.<anonymous> (src/render/render_to_texture.test.ts:136:20)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

render to texture › render symbol between rtt layers

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at RenderPool._createObject (src/gl/render_pool.ts:43:25) at RenderPool.getOrCreateFreeObject (src/gl/render_pool.ts:76:26) at RenderToTexture.renderLayer (src/render/render_to_texture.ts:170:39) at Object.<anonymous> (src/render/render_to_texture.test.ts:149:20)

Check failure on line 138 in src/render/texture.ts

View workflow job for this annotation

GitHub Actions / Unit tests and Coverage

render to texture › render more symbols between rtt layers

ReferenceError: WebGL2RenderingContext is not defined at Texture.textureFormatFromInternalFormat (src/render/texture.ts:138:9) at Texture.update (src/render/texture.ts:74:29) at new Texture (src/render/texture.ts:41:14) at RenderPool._createObject (src/gl/render_pool.ts:43:25) at RenderPool.getOrCreateFreeObject (src/gl/render_pool.ts:76:26) at RenderToTexture.renderLayer (src/render/render_to_texture.ts:170:39) at Object.<anonymous> (src/render/render_to_texture.test.ts:161:20)
return format;
}

switch (internalFormat) {
case WebGL2RenderingContext['RG8']:
format = WebGL2RenderingContext['RG'];
break;
case WebGL2RenderingContext['R8']:
format = WebGL2RenderingContext['RED'];
break;
}
return format;
}
}
18 changes: 15 additions & 3 deletions src/source/raster_tile_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ResourceType} from '../util/request_manager';
import {Event, ErrorEvent, Evented} from '../util/evented';
import {loadTileJson} from './load_tilejson';
import {TileBounds} from './tile_bounds';
import {Texture} from '../render/texture';
import {Texture, TextureFormat} from '../render/texture';

import type {Source} from './source';
import type {OverscaledTileID} from './tile_id';
Expand Down Expand Up @@ -66,6 +66,7 @@ export class RasterTileSource extends Evented implements Source {
_loaded: boolean;
_options: RasterSourceSpecification | RasterDEMSourceSpecification;
_tileJSONRequest: AbortController;
_textureFormat: TextureFormat;

constructor(id: string, options: RasterSourceSpecification | RasterDEMSourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) {
super();
Expand All @@ -80,11 +81,20 @@ export class RasterTileSource extends Evented implements Source {
this.scheme = 'xyz';
this.tileSize = 512;
this._loaded = false;
this._textureFormat = WebGLRenderingContext.RGBA;

this._options = extend({type: 'raster'}, options);
extend(this, pick(options, ['url', 'scheme', 'tileSize']));
}

set textureFormat(format: TextureFormat) {
this._textureFormat = format;
}

get textureFormat(): TextureFormat {
return this._textureFormat;
}

async load() {
this._loaded = false;
this.fire(new Event('dataloading', {dataType: 'source'}));
Expand Down Expand Up @@ -189,10 +199,12 @@ export class RasterTileSource extends Evented implements Source {
const gl = context.gl;
const img = response.data;
tile.texture = this.map.painter.getTileTexture(img.width);

if (tile.texture) {
tile.texture.update(img, {useMipmap: true});
// We need to update the format since tile textures are being reused by various sources
tile.texture.update(img, {useMipmap: true, format: this.textureFormat});
} else {
tile.texture = new Texture(context, img, gl.RGBA, {useMipmap: true});
tile.texture = new Texture(context, img, this.textureFormat, {useMipmap: true});
tile.texture.bind(gl.LINEAR, gl.CLAMP_TO_EDGE, gl.LINEAR_MIPMAP_NEAREST);
}
tile.state = 'loaded';
Expand Down
3 changes: 1 addition & 2 deletions src/util/web_worker_transfer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {SerializedObject} from '../../dist/maplibre-gl';
import {AJAXError} from './ajax';
import {register, serialize, deserialize} from './web_worker_transfer';
import {register, serialize, deserialize, SerializedObject} from './web_worker_transfer';

describe('web worker transfer', () => {
test('round trip', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/util/web_worker_transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {isImageBitmap} from './util';
/**
* A class that is serialized to and json, that can be constructed back to the original class in the worker or in the main thread
*/
type SerializedObject<S extends Serialized = any> = {
export type SerializedObject<S extends Serialized = any> = {
[_: string]: S;
};

Expand Down
2 changes: 1 addition & 1 deletion test/build/min.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('test min build', () => {
const decreaseQuota = 4096;

// feel free to update this value after you've checked that it has changed on purpose :-)
const expectedBytes = 877777;
const expectedBytes = 879038;

expect(actualBytes).toBeLessThan(expectedBytes + increaseQuota);
expect(actualBytes).toBeGreaterThan(expectedBytes - decreaseQuota);
Expand Down
Loading