npm package: @linzjs/landonline-openlayers-map
Description: This library has been designed to wrap a domain agnostic OpenLayers map component to behave more like a React component. This gives the advantages of creating a separation of concerns between the host application and the map component. In addition, isolat
Latest version: 6.2.8

# Landonline OpenLayers map (Lolol)

This library has been designed to wrap a domain agnostic OpenLayers map component to behave more like a React component. This gives the advantages of creating a separation of concerns between the host application and the map component. In addition, isolating the map enables provides a component for more targeted automated testing.

### Storybook
A number of stories have been created to demonstrate the capabilities and usage of this component. These can be found [here](https://master--62857a72236650004a029dc1.chromatic.com)

### Component
A component LolOpenLayersMap was created, based on the map created as part of the survey application. This manages a native openlayers `Map` object, which is associated with a `<div>` in the DOM.
```
/**
  * Properties for {@link LolOpenLayersMap}
  */
  export interface LolOpenLayersMapProps {
    /** children will be inserted into the DOM after the `map` div */
    children?: ReactNode;
    /** Defines the view with initial viewport coordinates, projection and scale */
    view: LolOpenLayersViewDef;
    /** Provide a setter to put the openlayers `Map` into state or global */
    setMap?: (map: Map) => void;
    /** Defines the Tile, VectorTile and Vector layers that make up the map */
    layers: LolOpenLayersLayerDef[];
    /** Group layers into a collection that are handled together in Openlayers,
     * see https://openlayers.org/en/latest/apidoc/module-ol_layer_Group-LayerGroup.html
     */
    layerGroup?: LolOpenLayersLayerGroupDef;
    /** Indicated if the map component should manage the loading indicator */
    useLoadingIndicator?: boolean;
    /** Use the QueryClient of the parent - required if using queries*/
    queryClient?: QueryClient;
    /** Buffer the default zoom to bufferfactor, e.g. 1.1 leaves a 10% margin, default is 1.05 **/
    bufferFactor?: number;
    /** Interactions for draw **/
    drawInteraction?: LolOpenLayersDrawInteraction;
    /** Interactions for snap **/
    snapInteraction?: LolOpenLayersSnapInteraction;
    /** Interactions for modify **/
    modifyInteraction?: LolOpenLayersModifyInteraction;
    /** For testing, mock the map rather than using Openlayers **/
    mock?: boolean;
    /** Enable an openlayers Overlay which can have its element accessed (to set contents, etc)
    * with mapContext.getOverlayRef
    * https://openlayers.org/en/latest/apidoc/module-ol_Overlay-Overlay.html
    */
    withOverlay?: boolean;
    /** Show default zoom control or not */
    withDefaultZoom?: boolean;
    /** disable zoom on double click */
    disableDoubleClickZoom?: boolean;
    /** Map z index */
    mapZIndex?: number;
    /** enable auto placement */
    enableAutoPlacement?: boolean;
    /** Show only placed labels, hide unplaced */
    showOnlyPlacedLabels?: boolean;
  }

/**
  * React wrapper for the openlayers https://openlayers.org/en/latest/apidoc/ library
  * to render interactive maps
  *
  * @param props The React properties
  */
  export const LolOpenLayersMap = (props: LolOpenLayersMapProps): ReactElement
```

Layers within the map can be defined as `LolOpenLayersLayerDef` which has subclasses:

- `LolOpenLayersTileLayerDef`: defines a tile layer, used for basemap, has LolOpenLayersXYZSourceDef to define a source against e.g. services.arcgisonline.com
- `LolOpenLayersVectorTileLayerDef`: defines a vector tile layer, has LolOpenLayersMVTSourceDef to define a source for use with Geoserver.
- `LolOpenLayersVectorLayerDef`: defines a vector layer. Source feature data can be loaded using LolOpenLayersFeatureSourceDef directly from an array of IFeatureSource, from a React query or loaded from state.

### Context
A context `LolOpenLayersMapContext` allows components at any level to interact with the state of the map.
```
export type LolOpenLayersMapContextType = {
  /** The status (visibility, etc) of each layer */
  layerStatus: (layerName: string) => ILayerStatus;
  setLayerStatus: (layerName: string, layerStatus: ILayerStatus) => void;
  /** The features dispalyed on a vector layer */
  featureData: (layerName: string) => IFeatureSource[] | undefined;
  setFeatureData: (layerName: string, features: IFeatureSource[]) => void;
  /** Ids of features that are selected, can be changed using set or interactively in the map */
  setFeatureSelect: (layerName: string, selectIds: number[]) => void;
  featureSelect: (layerName: string) => number[];
  /** Incrementing this causes the map to zoom to fit identified layers */
  zoomToFit: number;
  setZoomToFit: React.Dispatch<React.SetStateAction<number>>;
  /** The resolution (e.g. scale) of the map */
  mapResolution: number;
  setMapResolution: (r: number) => void;

  /** Used by LolOpenLayersMap to reflect when features have been changed */
  featuresChanged: (layerName: string) => boolean;
  clearFeaturesChanged: (layerName: string) => void;
  clearFeatureSelectChanged: (layerName: string) => void;
  featureSelectChanged: (layerName: string) => boolean;
};
```

### Development

#### Using a local copy

If you want to work on this module in the context of your app (e.g https://github.com/linz/landonline-survey) some tweakage is needed:
\[ Don't commit these, the pipeline won't like it \]

- Checkout this repo, branch and edit as desired.
- In your app:
- ` npm i --legacy-peer-deps -S <path>\landonline-openlayers-map`
- ` npm link --legacy-peer-deps -S <path>\landonline-openlayers-map\node_modules\react`

(The last step ensures that React uses the version in lolmap and we don't get a hook error. And fixing react-dates might avoid this).

Alternatively, use `npm pack` in this module to create a tgz file and import this into the application/module by a `file://` path.

#### Logging

This library uses the [loglevel](https://www.npmjs.com/package/loglevel) package for logging.

To enable logging from a caller, use e.g: 
```
logOpenlayersMap.setLevel(logOpenlayersMap.levels.DEBUG);
```

To add logs inside the library, import and use `log`.

#### Releases

This library uses [semantic-release](https://github.com/semantic-release/semantic-release) to manage version numbers.

The commit message on master drives the release:

| Commit message                                                                                                                                                                                                 | Release type               |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -------------------------- |
| `fix(landonline-openlayers-map): fix a bug with no API change`                                                                                                                                                 | ~~Patch~~ Fix Release      |
| `feat(landonline-openlayers-map): enhance with no breaking change`                                                                                                                                             | ~~Minor~~ Feature Release  |
| `perf(landonline-openlayers-map): make a breaking change`<br><br>`BREAKING CHANGE: The graphiteWidth option has been removed.`<br>`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release <br /> (Note that the `BREAKING CHANGE: ` token must be in the footer of the commit) |


### "@babel/plugin-proposal-private-property-in-object"

This is required because we use `eslint-config-react-app` which uses
 `babel-preset-react-app` which was causing warning

```
One of your dependencies, babel-preset-react-app, is importing the
"@babel/plugin-proposal-private-property-in-object" package without
declaring it in its dependencies. This is currently working because
"@babel/plugin-proposal-private-property-in-object" is already in your
node_modules folder for unrelated reasons, but it may break at any time.

is not maintianed anymore. It is thus unlikely that this bug will
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
your devDependencies to work around this error. This will make this message
go away.
```

If we remove `eslint-config-react-app` can safely be removed `@babel/plugin-proposal-private-property-in-object`

# Changes

## 5.2.0

This upgrades OpenLayers to 7.5.2

The following changes in the caller may be required:

- Enumerated types have been replaced by allowable string types in Openlayers, so you may need to enum constants to strings and remove the type ref

    Examples:

    * `GeometryType`
    * `OverlayPositioning`
    * `IconAnchorUnits`
  
- The options to some constructors now need a cast:

  * `DrawOptions`
  * `CanvasTextAlign`

- The `Map` component calls `ResizeObserver` which is not implemented in jest-dom.

    You should include the `resize-observer-polyfill` NPM module and add in setupTests.ts: 
`global.ResizeObserver = require("resize-observer-polyfill");`