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

Support for multiple map renderers #134

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
18,765 changes: 18,161 additions & 604 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"js-file-download": "^0.4.11",
"jshint": "^2.11.0",
"mapbox-gl": "^1.9.1",
"maplibre-gl": "^2.3.0",
"react": "^16.13.1",
"react-beautiful-dnd": "^13.0.0",
"react-dom": "^16.13.1",
Expand Down
80 changes: 80 additions & 0 deletions src/component/Field/FieldRadio.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react'
import PropTypes from 'prop-types'

import modelLayer from '../../model/layer'

class FieldRadio extends React.Component {

handleChange = (e)=>{
const {handle, name} = this.props
handle.change && handle.change({
name: name,
value: e.target.value
})
}

handleFocus = (e)=>{
const {handle, name} = this.props
handle.focus && handle.focus(name)
}

handleBlur = (e)=>{
const {handle, name} = this.props
handle.blur && handle.blur(name)
}

handleSubmit = async ()=>{
const {layer, path, style} = this.props
await modelLayer.actions.clone({layer, path, style})
}

render (){
const {helper, label, name, options, value} = this.props

return <div className="form-group mb-0">
{label && <label className="mb-0">{label}</label>}
<div>
{options.map((option)=>{
const optionId = `field-radio-${name}-${option.value?.replaceAll(' ', '_')}`

return <div key={option.value} className="form-check">
<input
name={name}
value={option.value}
checked={value === option.value}
type="radio"
className="form-check-input"
id={optionId}
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<label className="form-check-label" htmlFor={optionId}>{option.name}</label>
{option.helper && <div className="form-text font-med text-muted">
{option.helper}
</div>}
</div>
})}
</div>
{helper && <small className="form-text text-muted">{helper}</small>}
</div>
}
}

FieldRadio.propTypes = {
error: PropTypes.string,
handle: PropTypes.shape({
backout: PropTypes.func,
blur: PropTypes.func,
change: PropTypes.func,
focus: PropTypes.func,
}),
helper: PropTypes.string,
label: PropTypes.string,
name: PropTypes.string.isRequired,
options: PropTypes.array,
value: PropTypes.any,
}


export default FieldRadio
5 changes: 4 additions & 1 deletion src/component/Field/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import FieldJSON from './FieldJSON'
import FieldNumber from './FieldNumber'
import FieldSelect from './FieldSelect'
import FieldString from './FieldString'
import FieldRadio from './FieldRadio'

class Field extends React.Component {

Expand All @@ -32,6 +33,8 @@ class Field extends React.Component {
return <FieldString {...this.props}/>
case 'json':
return <FieldJSON {...this.props}/>
case 'radio':
return <FieldRadio {...this.props}/>
default:
return <FieldString {...this.props}/>
}
Expand All @@ -43,4 +46,4 @@ Field.propTypes = {
handle: PropTypes.object,
}

export default Field
export default Field
3 changes: 2 additions & 1 deletion src/component/Icon/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const icons = {
update: 'fa-wrench',
upload: 'fa-folder-upload',
visible: 'fa-eye',
renderer: 'fa-vector-square',
}

const weights = {
Expand All @@ -63,4 +64,4 @@ export default {
icons,
weightDefault,
weights,
}
}
2 changes: 1 addition & 1 deletion src/component/LayerAdd/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class LayerAdd extends React.Component {
}

render (){
const {error, style} = this.props,
const {style} = this.props,
{rec} = this.state

const typeOptions = modelLayer.helpers.getTypeOptions()
Expand Down
41 changes: 32 additions & 9 deletions src/component/Map/MapMapbox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {withRouter} from 'react-router-dom'
import modelMap from '../../model/map'
import modelStyle from '../../model/style'

import MapboxGl from 'mapbox-gl'
import 'mapbox-gl/dist/mapbox-gl.css'

import constants from './constants'
import utilUrl from '../../utility/utilUrl'
import utilRenderer from '../../utility/utilRenderer'

import Dropdown from '../Dropdown'
import MapMapboxControls from './MapMapboxControls'
Expand Down Expand Up @@ -74,6 +74,20 @@ class MapMapbox extends React.Component {
await modelMap.actions.setFeatureStateDeploy({deploy: false})
}

getRendererNameOrDefault (style){
return style?.get('metadata')?.get('fresco:renderer') || utilRenderer.defaultRenderer
}

getMapRenderer (){
const renderer = this.getRendererNameOrDefault(this.props.style)
let MapRenderer = utilRenderer.availableRenderers[renderer]
if (!MapRenderer) {
console.warn(`Renderer "${renderer}" is not a recognized option. Falling back to "${utilRenderer.defaultRenderer}".`)
MapRenderer = utilRenderer.availableRenderers[utilRenderer.defaultRenderer]
}
return MapRenderer
}

buildMap (){
const {accessTokens, style} = this.props
if (!style) return <div/>
Expand All @@ -83,7 +97,9 @@ class MapMapbox extends React.Component {
this.map.remove()
}

if (accessTokens && accessTokens.has('mapbox')) MapboxGl.accessToken = accessTokens.get('mapbox')
const MapRenderer = this.getMapRenderer()

if (accessTokens && accessTokens.has('mapbox')) MapRenderer.accessToken = accessTokens.get('mapbox')

window.onerror = (message, source, lineno, colno, error)=>{
const errString = JSON.stringify(error)
Expand All @@ -96,7 +112,7 @@ class MapMapbox extends React.Component {
})
}

const map = new MapboxGl.Map({
const map = new MapRenderer.Map({
attributionControl:false,
logoPosition:'bottom-right',
container: this.container,
Expand All @@ -110,11 +126,11 @@ class MapMapbox extends React.Component {

map.addControl(Controls)

map.addControl(new MapboxGl.AttributionControl({
map.addControl(new MapRenderer.AttributionControl({
compact: true
}))

const nav = new MapboxGl.NavigationControl()
const nav = new MapRenderer.NavigationControl()
map.addControl(nav, 'top-right')

map.on('error',(e)=>{
Expand All @@ -139,7 +155,7 @@ class MapMapbox extends React.Component {
this.map = map
}

componentDidUpdate = async ()=>{
componentDidUpdate = async (previousProps)=>{
const {accessTokenDeploy, focus, rebuildMap, style} = this.props,
{mapLoaded} = this.state

Expand All @@ -153,13 +169,13 @@ class MapMapbox extends React.Component {

if (!window.location.hash){
if (window.history.replaceState) {
window.history.replaceState(null, null, this.hash)
window.history.replaceState(null, null, this.hash)
}
else {
window.location.hash = this.hash
}
}
// if there is a point, set it on the map and query features
if (focus){
const featuresDuped = modelMap.helpers.queryMapFeatures({
Expand All @@ -185,7 +201,8 @@ class MapMapbox extends React.Component {
el.innerHTML = `<span class="marker-number"><span>${features.length}</span><span class="marker-close" onclick="window.mapMarkerClose(event)"><i class="fas fa-times"></i></span></span><i class="fas fa-map-marker text-info"></i>`
el.className = 'marker'
if (this.clickMarker) this.clickMarker.remove()
this.clickMarker = new MapboxGl.Marker(el, {offset:[0,-17]})
const MapRenderer = this.getMapRenderer()
this.clickMarker = new MapRenderer.Marker(el, {offset:[0,-17]})
.setLngLat(focus)
.addTo(this.map)
} else {
Expand All @@ -205,6 +222,12 @@ class MapMapbox extends React.Component {

await modelMap.actions.setAccessTokenDeploy({deploy: false})
}

const currentRenderer = this.getRendererNameOrDefault(style)
const previousRenderer = this.getRendererNameOrDefault(previousProps.style)
if (currentRenderer !== previousRenderer) {
this.buildMap()
}
}

componentWillUnmount(){
Expand Down
3 changes: 1 addition & 2 deletions src/component/SourceAdd/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ class SourceAdd extends React.Component {
}

render (){
const {error} = this.props,
{headers, makeLayers, id, type, url} = this.state
const {headers, makeLayers, id, type, url} = this.state

const options = utilMapboxSpec.getSourceTypeOptions()

Expand Down
9 changes: 9 additions & 0 deletions src/component/Style/StyleSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import StyleSettingsActions from './StyleSettingsActions'
import StyleSettingsDomains from './StyleSettingsDomains'
import StyleSettingsRoot from './StyleSettingsRoot'
import StyleSettingsTokens from './StyleSettingsTokens'
import StyleSettingsRenderers from './StyleSettingsRenderers'
import StyleUpdate from '../StyleUpdate'

class StyleRoot extends React.Component {
Expand Down Expand Up @@ -85,6 +86,12 @@ class StyleRoot extends React.Component {
</div>
domains
</NavLink>
<NavLink to={`${match.url}/renderers`} className="content-body-left-row row-icons">
<div className="row-icon-left">
<Icon className="md-shadow" icon={'renderer'} weight={'regular'}/>
</div>
renderers
</NavLink>
</div>
</div>
{this.renderRight()}
Expand Down Expand Up @@ -112,6 +119,8 @@ class StyleRoot extends React.Component {
render={(props) => <StyleSettingsDomains path={path} style={style} {...props}/>}/>
<Route path={`${match.url}/tokens`}
render={(props) => <StyleSettingsTokens path={currentPath} style={style} {...props}/>}/>
<Route path={`${match.url}/renderers`}
render={(props) => <StyleSettingsRenderers path={currentPath} style={style} {...props}/>}/>

<Redirect to={redirect}/>
</Switch>
Expand Down
77 changes: 77 additions & 0 deletions src/component/Style/StyleSettingsRenderers.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React from 'react'
import PropTypes from 'prop-types'
import {Map} from 'immutable'
import {withRouter} from 'react-router-dom'

import utilMapboxSpec from '../../utility/utilMapboxSpec'
import utilRenderer from '../../utility/utilRenderer'

import Property from '../Property'
import PropertyAdd from '../Property/PropertyAdd'
import Field from '../Field'

import modelStyle from '../../model/style'

const RENDERER_OPTIONS = [
{
value: 'mapbox-gl-v1',
name: 'Mapbox GL v1',
helper: <React.Fragment>
This is the original Mapbox vector tile rendering library. It is no longer actively developed. This is the default renderer. <a href='https://github.com/mapbox/mapbox-gl-js/tree/v1.13.2' target='_blank'>See details.</a>
</React.Fragment>,
},
{
value: 'maplibre-gl-v2',
name: 'MapLibre GL v2',
helper: <React.Fragment>
This is an open source fork of the Mapbox renderer after Mapbox adopted a proprietary model for v2. <a href='https://github.com/maplibre/maplibre-gl-js' target='blank'>See details.</a>
</React.Fragment>,
},
]

class StyleSettingsRenderers extends React.Component {
handleRendererChange = async ({value})=>{
const {style} = this.props
await modelStyle.actions.setRenderer({
style,
renderer: value,
})
}

render (){
const {style} = this.props

const current = style.get('current')

const currentRenderer = current.get('metadata')?.get('fresco:renderer') || utilRenderer.defaultRenderer

return (
<React.Fragment>
<h2 className="content-title content-title-sub content-title-light">
<span className="content-title-label">Style Renderers</span>
</h2>
<div className="property-content">
<Field
type='radio'
name='renderer'
value={currentRenderer}
options={RENDERER_OPTIONS}
handle={{
change: this.handleRendererChange,
}}
/>
</div>
</React.Fragment>
)
}
}

StyleSettingsRenderers.propTypes = {
error: PropTypes.object,
history: PropTypes.object,
location: PropTypes.object,
path: PropTypes.array,
style: PropTypes.object,
}

export default withRouter(StyleSettingsRenderers)
4 changes: 2 additions & 2 deletions src/model/source/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const makeLayersFromData = async ({sourceId, sourceData})=>{

const sourceLayers = helpers.getLayersFromData({data: sourceData})

sourceLayers.map((sourceLayer)=>{
sourceLayers.forEach((sourceLayer) => {
const color = utilMaterialColor.getBright(sourceLayer.get('id'))

let layer = {
Expand Down Expand Up @@ -156,4 +156,4 @@ export default {
init,
makeLayersFromData,
pullData,
}
}
Loading