# CESI - Tiles vs Features
## Layer Restrictions
Each layer type has trade-offs. And a layer can't shift to a different type once it's made.
To have tiles pulling down, you need a tile layer on the map. This will paint the image of polygons on the map super fast. But you lose identify, you lose maptips, and if user switches to different basemap projection, the layer won't draw.
Feature Layer restores all those losses, but at the cost of pulling tons of data.
## Map Swapping
Considering just the map, to have a tile visually swap to a feature layer after a certain zoom level, you would need to monitor the `'map/scalechanged'` event and react when your scale threshold is crossed. Possibly removing one layer and adding the other. And would also want to track the projection and monitor `'map/basemapchanged'` as you'd only want the feature layer (at all scales) when not in Lambert.
## Complex Hybrid
However if you want identify and "open table from legend" to be working even when the tile is on, will require a lot more trickery / hackery. A few initial ideas on how to do that.
### Stacked Layers
Have both the Tile Layer and Feature Layer added to the map at the same time. Don't put the Tile in the legend (or else you'll have two entries).
To make this work, some changes and a bit of R&D needs to happen.
- See if we can make a [minScale](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html#minScale) be defined locally on our Feature Layer. This will stop it from drawing until we zoom past the tile's limit.
- If this works, the minScale would be defined in the layer config (new properties; should support max as well) and applied on the ESRI config that creates the Feature Layer (to prevent the initial draw from firing).
- If we can't get it to work locally, will need to define the minScale on the standard CESI CA service. I don't think we want to do that.
- Requires republish
- If the service is used elsewhere people might not be pleased it won't draw at all scales.
- Want to draw all levels on Mercator side; this breaks that.
- Another alternative is having two services, but now we have 3 services for one dataset (Tile, Feature min, Feature all). :sideyes:
- We likely need a new configuration flag that allows an invisible layer to show Identify results, and show in the grid. Right now RAMP has a hard rule of "if you can't see the data on the map, we won't show it anywhere else". Without this new flag, if you opened CA table when the Feature layer is offscale, you'd get empty grid, and no results when clicking.
Projection change throws in another wrinkle.
- If off Lambert, the Tile should simply not draw, and since it is not in the legend, will not show a red entry. (Good thing).
- However it will raise notifications in the notifier tray. Not good. Ways to get around this:
- Have another config flag to allow a layer to suppress warnings. Double-edged option; maybe just specifically for tile layers.
- Refactor how changing a basemap works. Will now have a requesting event and default event handler. CESI overrides the event handler to intercept a schema change, removes the tile layer before continuing.
- That said, you now want the Feature layer drawing at all scales. So more R&D:
- can a `minScale` be changed at runtime?
- If no, then likely a full layer swap needs to happen during projection (remove the tile & feature, add in a feature with no `minScale`, or reverse if going to Lambert).
One drawback is the re-ordering fixture. We either need to show two layers there, or we tag the Tile layer as `cosmetic`, meaning it won't show in the list, but also cannot be re-ordered on the map.
After writing all this, I'm also noticing the Tile service on prod goes all the way down to street level. Which means we'd need to set a `maxScale` locally on the Tile layer to make it stop drawing. So the R&D for locally applying scale limits is clutch for this solution.
### Magical Legend
This is similar to Stacked Layers, but involves building a new, custom legend item control that acts as a "layer manager". It presents itself as a normal layer to the user, but inside it can manage the visibility for multiple layers according to scale levels, and pipe the "grid open" request to the correct real layer.
This magical legend would also need to be aware of the projection, which makes the concept far uglier.
We would still need the "invisible layer shows data" mod from Stacked Layers solution. The re-order drawback will still exists as well.
I'd only suggest this route if we think the scenario of having to morph between Tile & Feature will be a common one.
### Hybrid RAMP Layer
This pivots a bit from the original idea. In this one, we only use a Tile layer; No Feature Layer ever appears in the map. But we write a custom RAMP layer that manages feature / attributes, similar to how a Map Image Layer parent does.
This would only work for a tile that sources its interesting content from one feature class, and where the Tile Service exposes the underlying feature data. The RAMP layer then overrides the various methods and properties to route to the Feature Layer service. Likely requires a lot of code duplication (perhaps we can get clever), but once set up the layer should just play nice with the RAMP core.
In other words, its like a Tile Layer with attribute stuff enabled.
The primary concern with this idea is there is no zooming past the lowest boundary of tile cache. I did a quick test on the CA tile and the tiles tend to do an unpleasant fragmentation once past the limit, was hoping they'd just upscale and fuzz. We're a few patches behind on the ESRI API so maybe this is improved on the most recent. *Update*: noticed the CA tile service goes about 4 levels past where it stops having drawn tiles (lvl 15 vs 19). If I try a different tile with capped levels ([province borders](https://maps-cartes.ec.gc.ca/arcgis/rest/services/CESI_ICDE/Conserved_Areas/MapServer/)) it does the zoom & fuzz, so that might be the cause.
It would also not support maptips.
### Pipeline Overrides
I'm just handwaving on this one. I don't think it's a good idea and not even sure what's feasible without more research. Can deeper dive if the team loves the idea.
But basically you do the "layer swap" instead of the "layer stack", as outlined in the Map Swapping section above.
Then to get the grid and identify to work, the page code needs to delete the default event handlers for identify and open grid, and add new handlers. Those handlers would essentially go like
- Look at the incoming request.
- Any layers that are not CA, shuttle the request to the original pipeline
- If the layer is CA, and CA Tile is active
- Run custom web calls on the page to get results as JSON
- Format them to look like what RAMP expects
- Smash them into the original pipeline
My main concern is these things usually tie back to a layer, so we might need a proxy layer or something. Like opening the grid almost warrants making a feature layer. Identify might be able to fake it but the symbol and name field lookups could die. As you can see this is already getting mad-scientist level.
## Plan C
If after looking at all this, it may be decided that the perf gain from a stand-alone service with dedicated SOC is the saner solution, and gets the `GOOD ENOUGH` stamp.
Or at least good enough for time being and a future solution gets a more thorough treatment then me flinging ideas over a morning.
## Additional Note
If we restrict to one basemap schema, a lot of the problems simplify since we don't need to worry about a tile not aligning. Once again, Lambert Satellite would be :cherries: .
## Vector Tiles
A more experimental option for the future is the use of Vector Tiles. My assumption is the primary benefit will be minimal server load when requestion the Canada-wide views of the dataset. Everything is already pre-simplified for the scale, and any position detection work is faster (just needs to know tile position, not edges of complex shape).
I also expect the tiles to be schema-bound, similar to image tiles, so most of the projection change concerns still exist.
Really needs a working test service to do R&D on. Exactly how well it performs will dictate what level of investment is put into this idea.
## Flex Tile Layer
This isn't a full solution, but a proposal that may help ease a number of concerns above with what happens during a basemap schema change.
The idea is we make a RAMP layer that manages a set of URLs to tiles, each keyed to a tile schema id.
- When the layer is initially added, it spins up an ESRI layer using the URL for the active tileset.
- When a projection change occurs, the RAMP core can recognize a flex layer is present.
- Will remove the ESRI layer from the map stack prior to blasting the map view.
- Will re-add a new ESRI layer with the new URL after the map view has been re-created.
- If there is no URL for the schema, it just chills; no spamming of warnings (or we make this an option to show/hide).
This approach lets us keep a single RAMP layer (for pointer integrity) and internalizes what would be many event checks to a simple flag check in a couple of core routines.
This approach could also apply to both standard tiles and Vector tiles, so possibly some code sharing via a common baseclass can be leveraged.
This will do nothing in the quest to have a tile become a feature at some arbitrary moment.