import React from 'react' import {MdAddCircleOutline, MdDelete} from 'react-icons/md' import latest from '@maplibre/maplibre-gl-style-spec/dist/latest.json' import type {GeoJSONSourceSpecification, RasterDEMSourceSpecification, RasterSourceSpecification, SourceSpecification, StyleSpecification, VectorSourceSpecification} from 'maplibre-gl' import Modal from './Modal' import InputButton from './InputButton' import FieldString from './FieldString' import FieldSelect from './FieldSelect' import ModalSourcesTypeEditor, { EditorMode } from './ModalSourcesTypeEditor' import style from '../libs/style' import { deleteSource, addSource, changeSource } from '../libs/source' import publicSources from '../config/tilesets.json' type PublicSourceProps = { id: string type: string title: string onSelect(...args: unknown[]): unknown }; class PublicSource extends React.Component { render() { return
this.props.onSelect(this.props.id)} >

{this.props.title}

#{this.props.id}

} } function editorMode(source: SourceSpecification) { if(source.type === 'raster') { if(source.tiles) return 'tilexyz_raster' return 'tilejson_raster' } if(source.type === 'raster-dem') { if(source.tiles) return 'tilexyz_raster-dem' return 'tilejson_raster-dem' } if(source.type === 'vector') { if(source.tiles) return 'tilexyz_vector' return 'tilejson_vector' } if(source.type === 'geojson') { if (typeof(source.data) === "string") { return 'geojson_url'; } else { return 'geojson_json'; } } if(source.type === 'image') { return 'image'; } if(source.type === 'video') { return 'video'; } return null } type ActiveModalSourcesTypeEditorProps = { sourceId: string source: SourceSpecification onDelete(...args: unknown[]): unknown onChange(...args: unknown[]): unknown }; class ActiveModalSourcesTypeEditor extends React.Component { render() { return
#{this.props.sourceId} this.props.onDelete(this.props.sourceId)} style={{backgroundColor: 'transparent'}} >
} } type AddSourceProps = { onAdd(...args: unknown[]): unknown }; type AddSourceState = { mode: EditorMode sourceId: string source: SourceSpecification }; class AddSource extends React.Component { constructor(props: AddSourceProps) { super(props) this.state = { mode: 'tilejson_vector', sourceId: style.generateId(), source: this.defaultSource('tilejson_vector'), } } defaultSource(mode: EditorMode): SourceSpecification { const source = (this.state || {}).source || {} const {protocol} = window.location; switch(mode) { case 'geojson_url': return { type: 'geojson', data: `${protocol}//localhost:3000/geojson.json` } case 'geojson_json': return { type: 'geojson', cluster: (source as GeoJSONSourceSpecification).cluster || false, data: '' } case 'tilejson_vector': return { type: 'vector', url: (source as VectorSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json` } case 'tilexyz_vector': return { type: 'vector', tiles: (source as VectorSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], minzoom: (source as VectorSourceSpecification).minzoom || 0, maxzoom: (source as VectorSourceSpecification).maxzoom || 14 } case 'tilejson_raster': return { type: 'raster', url: (source as RasterSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json` } case 'tilexyz_raster': return { type: 'raster', tiles: (source as RasterSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], minzoom: (source as RasterSourceSpecification).minzoom || 0, maxzoom: (source as RasterSourceSpecification).maxzoom || 14 } case 'tilejson_raster-dem': return { type: 'raster-dem', url: (source as RasterDEMSourceSpecification).url || `${protocol}//localhost:3000/tilejson.json` } case 'tilexyz_raster-dem': return { type: 'raster-dem', tiles: (source as RasterDEMSourceSpecification).tiles || [`${protocol}//localhost:3000/{x}/{y}/{z}.pbf`], minzoom: (source as RasterDEMSourceSpecification).minzoom || 0, maxzoom: (source as RasterDEMSourceSpecification).maxzoom || 14 } case 'image': return { type: 'image', url: `${protocol}//localhost:3000/image.png`, coordinates: [ [0,0], [0,0], [0,0], [0,0], ], } case 'video': return { type: 'video', urls: [ `${protocol}//localhost:3000/movie.mp4` ], coordinates: [ [0,0], [0,0], [0,0], [0,0], ], } default: return {} as any } } onAdd = () => { const {source, sourceId} = this.state; this.props.onAdd(sourceId, source); } onChangeSource = (source: SourceSpecification) => { this.setState({source}); } render() { // Kind of a hack because the type changes, however maputnik has 1..n // options per type, for example // // - 'geojson' - 'GeoJSON (URL)' and 'GeoJSON (JSON)' // - 'raster' - 'Raster (TileJSON URL)' and 'Raster (XYZ URL)' // // So we just ignore the values entirely as they are self explanatory const sourceTypeFieldSpec = { doc: latest.source_vector.type.doc }; return
this.setState({ sourceId: v})} /> this.setState({mode: mode as EditorMode, source: this.defaultSource(mode as EditorMode)})} value={this.state.mode as string} /> Add Source
} } type ModalSourcesProps = { mapStyle: StyleSpecification isOpen: boolean onOpenToggle(...args: unknown[]): unknown onStyleChanged(...args: unknown[]): unknown }; export default class ModalSources extends React.Component { stripTitle(source: SourceSpecification & {title?: string}): SourceSpecification { const strippedSource = {...source} delete strippedSource['title'] return strippedSource } render() { const mapStyle = this.props.mapStyle const activeSources = Object.keys(mapStyle.sources).map(sourceId => { const source = mapStyle.sources[sourceId] return this.props.onStyleChanged(changeSource(mapStyle, sourceId, src))} onDelete={() => this.props.onStyleChanged(deleteSource(mapStyle, sourceId))} /> }) const tilesetOptions = Object.keys(publicSources).filter((sourceId: string) => !(sourceId in mapStyle.sources)).map((sourceId: string) => { const source = publicSources[sourceId as keyof typeof publicSources] as SourceSpecification & {title: string}; return this.props.onStyleChanged(addSource(mapStyle, sourceId, this.stripTitle(source)))} /> }) return

Active Sources

{activeSources}

Choose Public Source

Add one of the publicly available sources to your style.

{tilesetOptions}

Add New Source

Add a new source to your style. You can only choose the source type and id at creation time!

this.props.onStyleChanged(addSource(mapStyle, sourceId, source))} />
} }