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

Canvas source initialization from HTML element #6424

Merged
merged 8 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions debug/canvas.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,13 @@

var canvasStyle = {
"version": 8,
"sources": {
"canvas": {
"type": "canvas",
"canvas": "testCanvasID",
"coordinates": [
[-122.51596391201019, 37.56238816766053],
[-122.51467645168304, 37.56410183312965],
[-122.51309394836426, 37.563391708549425],
[-122.51423120498657, 37.56161849366671]
]
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can/should this be reverted now?

"sources": {},
"layers": [{
"id": "background",
"type": "background",
"paint": {
"background-color": "rgb(4,7,14)"
}
}, {
"id": "canvas",
"type": "raster",
"source": "canvas"
}]
};

Expand All @@ -71,6 +56,26 @@
hash: false
});

map.on('load', () => {
map.addSource('canvas', {
"type": "canvas",
"canvas": "testCanvasID",
"animate": false,
"coordinates": [
[-122.51596391201019, 37.56238816766053],
[-122.51467645168304, 37.56410183312965],
[-122.51309394836426, 37.563391708549425],
[-122.51423120498657, 37.56161849366671]
]
});

map.addLayer({
"id": "canvas",
"type": "raster",
"source": "canvas"
});
});

drawToCanvas();

</script>
Expand Down
1 change: 1 addition & 0 deletions docs/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ toc:
- VideoSource
- ImageSource
- CanvasSource
- CanvasSourceOptions
- name: Events
description: |
`Map` (and some other classes) emit events in response to user interactions or changes in state. `Evented`
Expand Down
69 changes: 2 additions & 67 deletions docs/pages/style-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import md from '../components/md';
import PageShell from '../components/page_shell';
import LeftNav from '../components/left_nav';
import TopNav from '../components/top_nav';
import {highlightJavascript, highlightJSON, highlightMarkup} from '../components/prism_highlight';
import {highlightJavascript, highlightJSON} from '../components/prism_highlight';
import entries from 'object.entries';
import ref from '../../src/style-spec/reference/latest';

Expand Down Expand Up @@ -97,9 +97,6 @@ const navigation = [
},
{
"title": "video"
},
{
"title": "canvas"
}
]
},
Expand Down Expand Up @@ -223,7 +220,7 @@ const navigation = [
}
];

const sourceTypes = ['vector', 'raster', 'raster-dem', 'geojson', 'image', 'video', 'canvas'];
const sourceTypes = ['vector', 'raster', 'raster-dem', 'geojson', 'image', 'video'];
const layerTypes = ['background', 'fill', 'line', 'symbol', 'raster', 'circle', 'fill-extrusion', 'heatmap', 'hillshade'];

const {expressions, expressionGroups} = require('../components/expression-metadata');
Expand Down Expand Up @@ -874,68 +871,6 @@ export default class extends React.Component {
</tbody>
</table>
</div>

<div id='sources-canvas' className='pad2 keyline-bottom'>
<h3 className='space-bottom1'><a href='#sources-canvas' title='link to canvas'>canvas</a></h3>
<p>
A canvas source. The <code>"canvas"</code> value is the ID of the canvas element in the document.
</p>
<p>
The <code>"coordinates"</code> array contains <code>[longitude, latitude]</code> pairs for the video
corners listed in clockwise order: top left, top right, bottom right, bottom left.
</p>
<p>
If an HTML document contains a canvas such as this:
</p>
<div className='space-bottom1 clearfix'>
{highlightMarkup(`<canvas id="mycanvas" width="400" height="300" style="display: none;"/>`)}
</div>
<p>
the corresponding canvas source would be specified as follows:
</p>
<div className='space-bottom1 clearfix'>
{highlightJSON(`
"canvas": {
"type": "canvas",
"canvas": "mycanvas",
"coordinates": [
[-122.51596391201019, 37.56238816766053],
[-122.51467645168304, 37.56410183312965],
[-122.51309394836426, 37.563391708549425],
[-122.51423120498657, 37.56161849366671]
]
}`)}
</div>
<p>
This source type is available only in Mapbox GL JS. Avoid using it in styles that need to maintain
compatibility with other Mapbox Maps SDKs.
</p>
<div className='space-bottom1 clearfix'>
{ entries(ref.source_canvas).map(([name, prop], i) =>
name !== '*' && name !== 'type' &&
<Item key={i} id={`sources-canvas-${name}`} name={name} {...prop}/>)}
</div>
<table className="micro">
<thead>
<tr className='fill-light'>
<th>SDK Support</th>
<td className='center'>Mapbox GL JS</td>
<td className='center'>Android SDK</td>
<td className='center'>iOS SDK</td>
<td className='center'>macOS SDK</td>
</tr>
</thead>
<tbody>
<tr>
<td>basic functionality</td>
<td>&gt;= 0.32.0</td>
<td>Not supported</td>
<td>Not supported</td>
<td>Not supported</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

Expand Down
8 changes: 0 additions & 8 deletions flow-typed/style-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,13 @@ declare type ImageSourceSpecification = {|
"coordinates": [[number, number], [number, number], [number, number], [number, number]]
|}

declare type CanvasSourceSpecification = {|
"type": "canvas",
"coordinates": [[number, number], [number, number], [number, number], [number, number]],
"animate"?: boolean,
"canvas": string
|}

declare type SourceSpecification =
| VectorSourceSpecification
| RasterSourceSpecification
| RasterDEMSourceSpecification
| GeojsonSourceSpecification
| VideoSourceSpecification
| ImageSourceSpecification
| CanvasSourceSpecification

declare type FillLayerSpecification = {|
"id": string,
Expand Down
47 changes: 44 additions & 3 deletions src/source/canvas_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,31 @@ import rasterBoundsAttributes from '../data/raster_bounds_attributes';
import VertexArrayObject from '../render/vertex_array_object';
import Texture from '../render/texture';
import { ErrorEvent } from '../util/evented';
import ValidationError from '../style-spec/error/validation_error';

import type Map from '../ui/map';
import type Dispatcher from '../util/dispatcher';
import type {Evented} from '../util/evented';

export type CanvasSourceSpecification = {|
"type": "canvas",
"coordinates": [[number, number], [number, number], [number, number], [number, number]],
"animate"?: boolean,
"canvas": string | HTMLCanvasElement
|};

/**
* A data source containing the contents of an HTML canvas.
* (See the [Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/#sources-canvas) for detailed documentation of options.)
* Options to add a canvas source type to the map.
*
* @typedef {Object} CanvasSourceOptions
* @property {string} type Source type. Must be `"canvas"`.
* @property {string|HTMLCanvasElement} canvas Canvas source from which to read pixels. Can be a string representing the ID of the canvas element, or the `HTMLCanvasElement` itself.
* @property {Array<Array<number>>} coordinates Four geographical coordinates denoting where to place the corners of the canvas, specified in `[longitude, latitude]` pairs.
* @property {boolean} [animate=true] Whether the canvas source is animated. If the canvas is static (i.e. pixels do not need to be re-read on every frame), `animate` should be set to `false` to improve performance.
*/

/**
* A data source containing the contents of an HTML canvas. See {@link CanvasSourceOptions} for detailed documentation of options.
*
* @example
* // add to map
Expand Down Expand Up @@ -56,6 +73,25 @@ class CanvasSource extends ImageSource {
*/
constructor(id: string, options: CanvasSourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) {
super(id, options, dispatcher, eventedParent);

// We build in some validation here, since canvas sources aren't included in the style spec:
if (!options.coordinates) {
this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, 'missing required property "coordinates"')));
} else if (!Array.isArray(options.coordinates) || options.coordinates.length !== 4 ||
options.coordinates.some(c => !Array.isArray(c) || c.length !== 2 || c.some(l => typeof l !== 'number'))) {
this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, '"coordinates" property must be an array of 4 longitude/latitude array pairs')));
}

if (options.animate && typeof options.animate !== 'boolean') {
this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, 'optional "animate" property must be a boolean value')));
}

if (!options.canvas) {
this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, 'missing required property "canvas"')));
} else if (typeof options.canvas !== 'string' && !(options.canvas instanceof window.HTMLCanvasElement)) {
this.fire(new ErrorEvent(new ValidationError(`sources.${id}`, null, '"canvas" must be either a string representing the ID of the canvas element from which to read, or an HTMLCanvasElement instance')));
}

this.options = options;
this.animate = options.animate !== undefined ? options.animate : true;
}
Expand All @@ -75,7 +111,12 @@ class CanvasSource extends ImageSource {
*/

load() {
this.canvas = this.canvas || window.document.getElementById(this.options.canvas);
if (!this.canvas) {
this.canvas = (this.options.canvas instanceof window.HTMLCanvasElement) ?
this.options.canvas :
window.document.getElementById(this.options.canvas);
}

this.width = this.canvas.width;
this.height = this.canvas.height;

Expand Down
1 change: 1 addition & 0 deletions src/source/image_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import VertexArrayObject from '../render/vertex_array_object';
import Texture from '../render/texture';

import type {Source} from './source';
import type {CanvasSourceSpecification} from './canvas_source';
import type Map from '../ui/map';
import type Dispatcher from '../util/dispatcher';
import type Tile from './tile';
Expand Down
17 changes: 12 additions & 5 deletions src/style-spec/error/validation_error.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@

function ValidationError(key, value, message) {
this.message = (key ? `${key}: ` : '') + message;
export default class ValidationError {
constructor(key, value, message) {
this.message = (key ? `${key}: ` : '') + message;

if (value !== null && value !== undefined && value.__line__) {
this.line = value.__line__;
if (value !== null && value !== undefined && value.__line__) {
this.line = value.__line__;
}
}
}

export default ValidationError;
export class ValidationWarning extends ValidationError {
constructor(key, value, message) {
super(key, value, message);
this.type = 'warning';
}
}
37 changes: 1 addition & 36 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@
"source_raster_dem",
"source_geojson",
"source_video",
"source_image",
"source_canvas"
"source_image"
],
"source_vector": {
"type": {
Expand Down Expand Up @@ -397,40 +396,6 @@
}
}
},
"source_canvas": {
"type": {
"required": true,
"type": "enum",
"values": {
"canvas": {
"doc": "A canvas data source."
}
},
"doc": "The data type of the canvas source."
},
"coordinates": {
"required": true,
"doc": "Corners of canvas specified in longitude, latitude pairs.",
"type": "array",
"length": 4,
"value": {
"type": "array",
"length": 2,
"value": "number",
"doc": "A single longitude, latitude pair."
}
},
"animate": {
"type": "boolean",
"default": "true",
"doc": "Whether the canvas source is animated. If the canvas is static, `animate` should be set to `false` to improve performance."
},
"canvas": {
"type": "string",
"required": true,
"doc": "HTML ID of the canvas from which to read pixels."
}
},
"layer": {
"id": {
"type": "string",
Expand Down
11 changes: 3 additions & 8 deletions src/style-spec/validate/validate_source.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import ValidationError from '../error/validation_error';
import ValidationError, { ValidationWarning } from '../error/validation_error';
import { unbundle } from '../util/unbundle_jsonlint';
import validateObject from './validate_object';
import validateEnum from './validate_enum';
Expand Down Expand Up @@ -65,13 +65,8 @@ export default function validateSource(options) {
});

case 'canvas':
return validateObject({
key: key,
value: value,
valueSpec: styleSpec.source_canvas,
style: style,
styleSpec: styleSpec
});
errors.push(new ValidationWarning(key, null, `Please use runtime APIs to add canvas sources, rather than including them in stylesheets.`));
return errors;

default:
return validateEnum({
Expand Down
2 changes: 1 addition & 1 deletion src/style/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ class Style extends Evented {
throw new Error(`The type property must be defined, but the only the following properties were given: ${Object.keys(source).join(', ')}.`);
}

const builtIns = ['vector', 'raster', 'geojson', 'video', 'image', 'canvas'];
const builtIns = ['vector', 'raster', 'geojson', 'video', 'image'];
const shouldValidate = builtIns.indexOf(source.type) >= 0;
if (shouldValidate && this._validate(validateStyle.source, `sources.${id}`, source, null, options)) return;

Expand Down
Loading