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

Proposed new API #24

Open
allthesignals opened this issue Dec 13, 2018 · 2 comments
Open

Proposed new API #24

allthesignals opened this issue Dec 13, 2018 · 2 comments

Comments

@allthesignals
Copy link
Collaborator

allthesignals commented Dec 13, 2018

// an invoked mapbox-gl map will detect sources from the layerGroup registry
// and have them available in sources
{{#mapbox-gl as |map|}}
    // add all layer groups
    {{#each map.layerGroupsRegistry.layerGroups as |layer-group|}}
		{{#map.layer-group layer-group.id
			onMouseOver=(action 'handleMouseOver') as |layer-group|}}
		{{/map.layer-group}}
	{{/each}}

    // add something specific from the registry by id name
	{{#map.layer-group 'census-blocks'
		onMouseOver=(action 'handleMouseOver') as |layer-group|}}
        // explicitly configure tooltips
		{{#layer-group.tooltip as |tooltip|}}
			The Title: {{tooltip.feature.properties.title}}
		{{/layer-group.tooltip}}
	{{/map.layer-group}}

	// add something static, local
	{{#map.layer-group coffeeShopLocationsLayerGroup
		onMouseOver=(action 'handleMouseOver') as |layer-group|}}
        // explicitly configure tooltips
		{{#layer-group.tooltip as |tooltip|}}
			Rating: {{tooltip.feature.properties.rating}}
		{{/layer-group.tooltip}}
	{{/map.layer-group}}
{{/mapbox-gl}}

I'm thinking we should move away from the data store. At first, using the store made a lot of sense because it was a data persistence service, but we've learned that it is sometimes overkill for our needs:

  1. We don't need layer-group models to persist changes to a database.

Ember Data was design to help with many common issues around create, update, and deleting resources. We, however, do not need those features. Most temporary updating is handled in the initial request, and ephemeral changes are simple property changes on the model.

  1. It's awkward to turn off sets of layers for specific maps

One thing we did not account for is the need for multiple maps across views that use layer groups. As the data store is a singleton, ephemeral changes may be carried across maps, which wouldn't make sense for the developer and certainly not the user.

  1. It's hard to statically load local configurations of layer groups

Currently our opinionated layer group definition makes it hard to simply add a layer group from local data. We rely heavily on a layers-api to manage these settings and validate structure. Adding a layer-group to a map has become a process of committing files to a separate service somewhere.

We do need some form of data modeling, however, but that can be handled with simple private object definitions that are created once layer-groups get invoked.

{{mapbox-gl}} may need some special handling for reverting object changes once components are destroyed. There are 3 forms of state:

  1. Default: typically whatever is stored on the layer-group service is the default
  2. App-level defaults: when a layer-group is requested, it is possible to override certain properties, allowing developers to change certain configuration, almost like subclassing.
  3. Component-level changes: when a user sets a filter or toggles off a layer, this is more the ephemeral state change.

This 3rd form of state doesn't currently get reverted out-of-the-box. When it is, we need to use Ember Data's "rollback attributes" feature. We could, however, have mapbox-gl handle this:

// ...
willDestroy() {
	this.layerGroupRegistry.resetAll();
}
// ...

Other issues:

  • "Highlighted layer" implementation is creating some problems for us. It's somewhere between mapbox-gl and layer-group because there can only be one thing ever highlighted.
@allthesignals
Copy link
Collaborator Author

  • Collate all current usages to better understand edge cases

@allthesignals
Copy link
Collaborator Author

Usage across 7 apps:
https://github.com/NYCPlanning/labs-waterfront-access

    {{#labs-map
      id='map'
      initOptions=(hash
        zoom=10
        center=(array -73.96532400540775 40.709710016659386)
        hash=true
      )
      layerGroups=model.layerGroups
      mapLoaded=(action 'handleMapLoad') as |map|
    }}
      {{#if geocodedFeature}}
        {{#map.source options=geocodedFeature as |source|}}
          {{source.layer layer=geocodedLayer}}
        {{/map.source}}
      {{/if}}

      {{#if shouldFitBounds}}
        {{map.call 'fitBounds' mainMap.bounds mainMap.isSelectedBoundsOptions}}
      {{/if}}

      {{#map.labs-layers
        onLayerClick=(action 'handleLayerClick')
        as |layers|}}
        {{#layers.tooltip as |tooltip|}}
          {{tooltip-renderer feature=tooltip.feature template=tooltip.layer.tooltipTemplate}}
        {{/layers.tooltip}}
      {{/map.labs-layers}}

      {{!-- Ad-hoc source and layer for developmentSite --}}
      {{#if highlightedFeature}}
        {{#map.source options=(mapbox-geojson-source highlightedFeature) as |source|}}
          {{source.layer
            layer=highlightedFeatureLayer
            before='boundary_country'
          }}
        {{/map.source}}
      {{/if}}

      {{map.on 'click' (action 'mapClicked')}}

      {{#if popupFeature}}
        {{#map.popup lngLat=popupLocation}}
          <div class="popup-content">
            {{#if popupParkName}}
              <h3 class="header-medium small-margin-bottom">{{popupParkName}}</h3>
            {{/if}}
            {{#if popupAgency}}
              <h5 class="header-small dark-gray small-margin-bottom">{{popupAgency}}</h5>
            {{/if}}
            {{#if popupLink}}
              <a href="{{popupLink}}" target="_blank" class="button small gray expanded no-margin">{{fa-icon 'external-link-alt'}} Learn more about this space</a>
            {{/if}}
          </div>
        {{/map.popup}}
      {{/if}}
    {{/labs-map}}

https://github.com/NYCPlanning/labs-applicantmaps

    {{#labs-map
      id='main-map'
      initOptions=(hash
        style=projectMapForm.model.meta.mapboxStyle
        zoom=12
        center=(array -73.983307 40.704977)
      )
      mapLoaded=(action 'handleMapLoad') as |map|}}

      {{search-handler
        map=map}}

      {{!-- labs-layers (includes tax-lots layer) --}}
      {{#map.labs-layers
        layerGroups=projectMapForm.model.layerGroups
        as |layers|}}
        {{#layers.tooltip as |tooltip|}}
          {{tooltip-renderer
            feature=tooltip.feature
            template=tooltip.layer.tooltipTemplate}}
        {{/layers.tooltip}}
      {{/map.labs-layers}}

      {{#map.source options=(mapbox-geojson-source model.developmentSite) as |source|}}
        {{source.layer
          layer=developmentSiteLayer
          before='boundary_country'
        }}
      {{/map.source}}

      {{#map.source options=(mapbox-geojson-source model.projectArea) as |source|}}
        {{source.layer
          layer=projectAreaLayer
          before='boundary_country'
        }}
      {{/map.source}}

      {{!-- invoke the component for the specified type --}}
      {{component projectGeometryType
        model=model
        mode=mode
        map=map}}
// ...
    {{/labs-map}}
          {{#labs-map
            id='main-map'
            initOptions=(hash
              style=mapConfiguration.meta.mapboxStyle

            )
            mapLoaded=(action 'handleMapLoaded') as |map|
          }}
            <div class="labs-map-loaded"></div>
            {{map.labs-layers layerGroups=mapConfiguration.layerGroups}}

            {{!-- Ad-hoc source and layer for showing commercial overlays --}}
            {{#if model.project.commercialOverlays}}
              {{#map.source options=(mapbox-geojson-source model.project.commercialOverlays) as |source|}}
                {{source.layer layer=projectGeomLayers.coLayer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c11Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c12Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c13Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c14Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c15Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c21Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c22Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c23Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c24Layer before='boundary_country'}}
                {{source.layer layer=projectGeomLayers.c25Layer before='boundary_country'}}
              {{/map.source}}
            {{/if}}

            {{!-- Ad-hoc source and layer for showing commercial overlays --}}
            {{#if model.project.proposedSpecialPurposeDistricts}}
              {{#map.source options=(mapbox-geojson-source model.project.proposedSpecialPurposeDistricts) as |source|}}
                {{source.layer
                  layer=projectGeomLayers.proposedSpecialPurposeDistrictsLayer
                  before='boundary_country'
                }}
                {{source.layer
                  layer=projectGeomLayers.proposedSpecialPurposeDistrictsLabelsLayer
                  before='boundary_country'
                }}
              {{/map.source}}
            {{/if}}

            {{#map.source options=(mapbox-geojson-source model.project.projectArea) as |source|}}
              {{source.layer
                layer=projectGeomLayers.projectAreaLayer
                before='boundary_country'
              }}
            {{/map.source}}

            {{#map.source options=(mapbox-geojson-source model.projectGeometryBuffer) as |source|}}
              {{source.layer
                layer=projectGeomLayers.projectBufferLayer
                before='boundary_country'
              }}
            {{/map.source}}

            {{#if model.project.rezoningArea}}
              {{#map.source options=(mapbox-geojson-source model.project.rezoningArea) as |source|}}
                {{source.layer
                  layer=projectGeomLayers.rezoningAreaLayer
                  before='boundary_country'
                }}
              {{/map.source}}
            {{/if}}

            {{#if model.project.underlyingZoning}}
              {{#map.source options=(mapbox-geojson-source model.project.underlyingZoning) as |source|}}
                {{source.layer
                  layer=projectGeomLayers.underlyingZoningLayer
                  before='boundary_country'
                }}
                {{source.layer
                  layer=projectGeomLayers.underlyingZoningLabelsLayer
                  before='boundary_country'
                }}
              {{/map.source}}
            {{/if}}

            {{#map.source options=(mapbox-geojson-source model.project.developmentSite) as |source|}}
              {{source.layer
                layer=projectGeomLayers.developmentSiteLayer
                before='boundary_country'
              }}
            {{/map.source}}

            {{map.on 'zoom' (action 'updateBounds')}}
            {{map.on 'drag' (action 'updateBounds')}}
            {{map.on 'pitch' (action 'updateBounds')}}
            {{map.on 'rotate' (action 'updateBounds')}}
          {{/labs-map}}

https://github.com/NYCPlanning/labs-factfinder

  {{#labs-map
    id='map'
    sources=sources
    initOptions=(hash zoom=zoom
                      center=center
                      hash=true)
    mapLoaded=(action 'handleMapLoad')
    as |map|}}

    {{#map.labs-layers
      onLayerClick=(action 'handleClick')
      layerGroups=model.layerGroups as |layers|}}
      {{#layers.tooltip as |tooltip|}}
        {{tooltip-renderer
          feature=tooltip.feature
          template=tooltip.layer.tooltipTemplate}}
      {{/layers.tooltip}}
    {{/map.labs-layers}}

    {{!-- TODO --}}
    {{map.layer layer=subduedNtaLabels}}

    {{#if selection.currentAddress}}
      {{#map.source sourceId='currentAddress' options=selection.addressSource as |source|}}
        {{source.layer layer=selection.pointLayer}}
      {{/map.source}}
    {{/if}}

    {{#if selection.searchResultFeature}}
      {{#map.source sourceId='searchResultSource' options=selection.searchResultSource as |source|}}
        {{source.layer layer=selection.searchResultLayer}}
      {{/map.source}}
    {{/if}}

    {{map.layer-group
      config=layerGroups.choropleths
      visible=false}}

    {{map.source
      sourceId='choropleths'
      options=choroplethsSource}}

    {{#if selection.current}}
      {{#map.source
        sourceId='selected-features'
        options=selectedSource as |source|}}
        {{source.layer
          layer=selectedFillLayer
          before='place_other'}}
        {{source.layer
          before='place_other'}}
      {{/map.source}}
    {{/if}}

    {{#if mapMouseover.highlightedFeature}}
      {{#map.source
        sourceId='highlighted-feature'
        options=mapMouseover.highlightedFeatureSource as |source|}}
        {{source.layer
          layer=highlightedFeature
          before='place_other'}}
      {{/map.source}}
    {{/if}}

    {{map.on 'draw.modechange' (action 'handleDrawModeChange')}}
    {{map.on 'draw.create' (action 'handleDrawCreate')}}

  {{/labs-map}}

https://github.com/NYCPlanning/labs-citymap

    {{#labs-map
      id='main-map'
      initOptions=initMapOptions
      mapLoaded=(action 'handleMapLoad') as |map|}}

      {{#map.labs-layers
        onLayerMouseMove=(action 'handleLayerMouseMove')
        layerGroups=model.layerGroups as |layers|}}
        {{#layers.tooltip as |tooltip|}}
          {{tooltip-renderer feature=tooltip.feature template=tooltip.layer.tooltipTemplate}}
        {{/layers.tooltip}}
      {{/map.labs-layers}}

      {{#if (not-eq popupFeatures null)}}
        {{#map.popup
          lngLat=popupLocation}}
          {{map-popup-content
            onHoverListItem=(action 'handlePopupLinkOver')
            onMouseLeave=(action 'clearAmendmentHover')
            features=popupFeatures}}
        {{/map.popup}}

        {{#if highlightedAmendmentSource}}
          {{#map.source options=highlightedAmendmentSource as |source|}}
            {{source.layer layer=(hash
                type='line'
                paint=(hash
                  line-color="#ffff00"
                  line-opacity=0.6
                  line-width=12
                )
                layout=(hash
                  line-cap='round'
                )
              )
              before='boundary_country'
            }}
            {{source.layer layer=(hash
                type='line'
                paint=(hash
                  line-color="rgba(60, 133, 210, 1)"
                  line-opacity=1
                  line-width=(hash
                    stops = (array
                      (array 10 1.5)
                      (array 14 5)
                    )
                  )
                )
                layout=(hash
                  line-cap='round'
                )
              )
              before='boundary_country'
            }}
          {{/map.source}}
        {{/if}}
      {{else}}
        {{#map.popup lngLat=popupLocation}}
          {{fa-icon 'spinner' class='fa-spin medium-gray fa-3x fa-fw'}}
        {{/map.popup}}
      {{/if}}

      {{#if highlightedStreetSource}}
        {{#map.source options=highlightedStreetSource as |source|}}
          {{source.layer layer=(hash
              type='line'
              paint=(hash
                line-color="#ffff00"
                line-opacity=0.6
                line-width=12
              )
              layout=(hash
                line-cap='round'
              )
            )
            before='boundary_country'
          }}
          {{source.layer layer=(hash
              type='line'
              paint=(hash
                line-color="rgba(60, 133, 210, 1)"
                line-opacity=1
                line-width=(hash
                  stops = (array
                    (array 10 1.5)
                    (array 14 5)
                  )
                )
              )
              layout=(hash
                line-cap='round'
              )
            )
            before='boundary_country'
          }}
        {{/map.source}}
      {{/if}}

      {{#if highlightedAmendmentSource}}
        {{#map.source options=highlightedAmendmentSource as |source|}}
          {{source.layer layer=(hash
              type='line'
              paint=(hash
                line-color="#ffff00"
                line-opacity=0.6
                line-width=12
              )
              layout=(hash
                line-cap='round'
              )
            )
            before='boundary_country'
          }}
          {{source.layer layer=(hash
              type='line'
              paint=(hash
                line-color="rgba(60, 133, 210, 1)"
                line-opacity=1
                line-width=(hash
                  stops = (array
                    (array 10 1.5)
                    (array 14 5)
                  )
                )
              )
              layout=(hash
                line-cap='round'
              )
            )
            before='boundary_country'
          }}
        {{/map.source}}
      {{/if}}

      {{#if searchedAddressSource}}
        {{#map.source options=searchedAddressSource as |source|}}
          {{source.layer layer=(hash
              type='circle'
              paint=(hash
                circle-radius=8
                circle-color="#00A1F9"
                circle-stroke-width=2
                circle-stroke-color="#FFFFFF"
              )
            )
            before='boundary_country'
          }}
        {{/map.source}}
      {{/if}}

      {{map.on 'render' (action 'handleMapLoading')}}
      {{map.on 'data' (action 'handleMapLoading')}}
      {{map.on 'sourcedata' (action 'handleMapLoading')}}
      {{map.on 'click' (action 'handleMapClick')}}
      {{map.on 'move' (action 'handleMapPositionChange')}}
      {{map.on 'mousemove' (action 'handleLayerMouseMove')}}
      {{map.on 'drag' (action 'handleMapMouseDrag')}}
      {{map.on 'zoomend' (action 'handleMapPositionChange')}}

      {{#if loadStateTask.isRunning}}
        {{fa-icon 'spinner' class='fa-spin fa-3x map-loading-spinner'}}
      {{/if}}

      <a class="zola-map-area-button" href="https://zola.planning.nyc.gov/{{mapLatLngZoomHash}}" target="_blank" rel="noopener">
        <strong>View <span class="show-for-medium">this area</span> in ZoLa</strong>
        <small class="show-for-medium">NYC’s Zoning &amp; Land Use Map</small>
      </a>
      {{#custom-controls
        shareURL=shareURL
        boundsGeoJSON=boundsGeoJSON}}
        <div class="print-controls mapboxgl-ctrl mapboxgl-ctrl-group">
          <button {{action 'handlePrint'}} class="mapboxgl-ctrl-icon">{{fa-icon 'print'}}</button>
        </div>
      {{/custom-controls}}

    {{/labs-map}}

https://github.com/NYCPlanning/labs-map-skeleton-app

    {{#labs-map
      id='map'
      initOptions=(hash
        zoom=10
        center=(array -73.96532400540775 40.709710016659386)
        hash=true
      )
      mapLoaded=(action 'handleMapLoad') as |map|
    }}
      <!--labs-layers from ember-mapbox-composer-->
      {{#map.labs-layers
        layerGroups=model.layerGroups
        as |layers|}}
          {{#layers.tooltip as |tooltip|}}
            {{tooltip-renderer feature=tooltip.feature template=tooltip.layer.tooltipTemplate}}
          {{/layers.tooltip}}
      {{/map.labs-layers}}

      {{#if geocodedFeature}}
        {{#map.source options=geocodedFeature as |source|}}
          {{source.layer layer=geocodedLayer}}
        {{/map.source}}
      {{/if}}
    {{/labs-map}}

https://github.com/NYCPlanning/labs-ui

    {{#labs-map id='map' mapLoaded=(action 'handleMapLoad') as |map|}}
      {{map.labs-layers layerGroups=model.layerGroups}}
    {{/labs-map}}

https://github.com/NYCPlanning/labs-zola

{{#labs-map
  id='main-map'
  layerGroups=layerGroups
  initOptions=(hash style=layerGroupsMeta.mapboxStyle
                    zoom=zoom
                    hash=true
                    center=(array lng lat))
  mapLoaded=(action 'handleMapLoad')
  as |map|}}
  <div class="labs-map-loaded" style="display:none"></div>
  {{#map.labs-layers
    interactivity=interactivity
    highlightedFeatureLayer=highlightedLotLayer
    onLayerClick=(action 'handleLayerClick')
    onLayerHighlight=(action 'handleLayerHighlight')
     as |layers|}}
    {{#layers.tooltip as |tooltip|}}
      {{tooltip-renderer feature=tooltip.feature template=tooltip.layer.tooltipTemplate}}
    {{/layers.tooltip}}
  {{/map.labs-layers}}

  {{#if bookmarkedLotsLayer}}
    {{map.layer
      layer=bookmarkedLotsLayer
      before='place_other'}}
  {{/if}}

  {{#if mainMap.selected}}
    {{#map.source
      sourceId='selected-lot'
      options=selectedLotSource as |source|}}
      {{source.layer
        layer=selectedFillLayer
        before='place_other'}}
      {{source.layer
        layer=selectedLineLayer
        before='place_other'}}
    {{/map.source}}
  {{/if}}

  {{#if mainMap.drawnFeature}}
    {{#map.source
      sourceId='drawn-feature'
      options=mainMap.drawnFeatureSource as |source|}}
      {{source.layer layer=drawnFeatureLayers.line before='place_other'}}
      {{#if (eq mainMap.drawnFeature.type 'Polygon')}}
        {{source.layer layer=drawnFeatureLayers.fill before='place_other'}}
      {{/if}}
    {{/map.source}}
  {{/if}}

  {{#if mainMap.currentAddress}}
    {{#map.source sourceId='currentAddress' options=mainMap.addressSource as |source|}}
      {{source.layer layer=mainMap.pointLayer}}
    {{/map.source}}
  {{/if}}

  {{map.on 'click' (action routeToLot)}}
  {{map.on 'data' (action 'mapLoading')}}
  {{map.on 'zoomend' (action 'handleZoomend')}}
  {{map.on 'mousemove' (action 'handleMousemove')}}
  {{map.on 'mouseout' (action 'handleMouseleave')}}
  {{map.on 'draw.create' (action 'handleDrawCreate')}}
  {{map.on 'draw.render' (action 'handleMeasurement')}}
{{/labs-map}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant