{"analyzedAt":"2023-01-08T21:19:25.847Z","collected":{"metadata":{"name":"rlayers","scope":"unscoped","version":"1.4.8","description":"React Components for OpenLayers","keywords":["react","maps","openlayers","gis","cartography","es6","js","typescript"],"date":"2023-01-08T21:19:11.569Z","author":{"name":"Momtchil Momtchev","email":"momtchil@momtchev.com","username":"mmomtchev"},"publisher":{"username":"mmomtchev","email":"momtchil@momtchev.com"},"maintainers":[{"username":"mmomtchev","email":"momtchil@momtchev.com"}],"repository":{"type":"git","url":"git+https://github.com/mmomtchev/rlayers.git"},"links":{"npm":"https://www.npmjs.com/package/rlayers","homepage":"https://mmomtchev.github.io/rlayers/","repository":"https://github.com/mmomtchev/rlayers","bugs":"https://github.com/mmomtchev/rlayers/issues"},"license":"ISC","dependencies":{"lru-cache":"^7.10.0"},"devDependencies":{"@rollup/plugin-commonjs":"^22.0.0","@rollup/plugin-node-resolve":"^13.3.0","@testing-library/jest-dom":"^5.11.9","@testing-library/react":"^13.0.0","@types/jest":"^27.4.1","@types/node":"^17.0.35","@types/prismjs":"^1.16.3","@types/proj4":"^2.5.2","@types/react":"^18.0.21","@types/react-dom":"^18.0.5","@typescript-eslint/eslint-plugin":"^5.26.0","@typescript-eslint/parser":"^5.26.0","@typescript-eslint/typescript-estree":"^5.26.0","bootstrap":"^5.1.3","copyfiles":"^2.4.1","css-loader":"^6.7.1","documentation":"^13.2.5","documentation-hipster":"^1.0.0","eslint":"^8.16.0","eslint-config-prettier":"^8.3.0","eslint-plugin-import":"^2.25.4","eslint-plugin-prettier":"^4.0.0","eslint-plugin-react":"^7.30.0","eslint-plugin-react-hooks":"^4.3.0","file-loader":"^6.2.0","fork-ts-checker-webpack-plugin":"^6.5.2","gh-pages":"^3.1.0","glob":"^8.0.1","html-loader":"^3.1.0","html-webpack-plugin":"^5.0.0","jest":"^28.1.0","jest-canvas-mock":"^2.3.1","jest-environment-jsdom":"^28.1.0","markdown-loader":"^8.0.0","null-loader":"^4.0.1","ol":"^7.2.2","prettier":"2.8.1","prism-themes":"^1.6.0","prismjs":"^1.28.0","proj4":"^2.7.0","raw-loader":"^4.0.2","react":"^18.1.0","react-docgen-typescript":"^2.1.0","react-dom":"^18.0.0","react-router":"^6.3.0","react-router-dom":"^6.3.0","react-test-renderer":"^18.1.0","resize-observer-polyfill":"^1.5.1","rimraf":"^3.0.2","rollup":"^2.74.1","rollup-plugin-terser":"^7.0.2","style-loader":"^3.3.1","svg-url-loader":"^7.1.1","ts-jest":"^28.0.3","ts-loader":"^9.3.0","ts-node":"^10.8.0","tsconfig-paths-webpack-plugin":"^4.0.0","typescript":"^4.4.3","webpack":"^5.20.1","webpack-cli":"^4.5.0","webpack-dev-server":"^4.0.0"},"peerDependencies":{"ol":">=6.10.0 <=7.2.2","react":">=16.8","react-dom":">=16.8"},"releases":[{"from":"2022-12-09T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":2},{"from":"2022-10-10T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":3},{"from":"2022-07-12T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":6},{"from":"2022-01-08T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":14},{"from":"2021-01-08T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":36}],"hasTestScript":true,"readme":"# rlayers - React Components for OpenLayers 6+\n\n![logo](https://raw.githubusercontent.com/mmomtchev/rlayers/master/rlayers-logo.svg)\n\n[![License: ISC](https://img.shields.io/github/license/mmomtchev/rlayers)](https://github.com/mmomtchev/rlayers/blob/master/LICENSE)\n[![npm version](https://img.shields.io/npm/v/rlayers)](https://www.npmjs.com/package/rlayers)\n[![Node.js CI](https://github.com/mmomtchev/rlayers/workflows/Node.js%20CI/badge.svg)](https://github.com/mmomtchev/rlayers/actions?query=workflow%3A%22Node.js+CI%22)\n[![codecov](https://codecov.io/gh/mmomtchev/rlayers/branch/master/graph/badge.svg)](https://codecov.io/gh/mmomtchev/rlayers)\n[![downloads](https://img.shields.io/npm/dm/rlayers)](https://www.npmjs.com/package/rlayers)\n\n![© OpenStreetMap contributors](https://gist.githubusercontent.com/mmomtchev/e789dfa545b97c7ae97770f1b5606172/raw/79b486bcc8c45b069e3a8f552c82360de80febff/ref1.png)\n![Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)](https://gist.githubusercontent.com/mmomtchev/e789dfa545b97c7ae97770f1b5606172/raw/79b486bcc8c45b069e3a8f552c82360de80febff/ref2.png)\n\n_rlayers_ is an opinionated set of _React_ components for _OpenLayers_.\n\nIt's design policy is:\n\n-   Fully Typescript-typed\n-   Do everything that faces the user the **_React_ way** and not the _OpenLayers_ way - `onClick` and `onPointerEnter`/`onPoinerLeave` handlers are typical examples\n-   If it does not face the user, it does not need to be _React_ way - internally it uses inheritance, following the _OpenLayers_ classes, over composition\n-   Simple things should be simple to do, performance optimizations should not get in the way unless needed\n-   If taking shortcuts when updating the components, always err on the safe side but do provide an override method that allows to come close to the raw OpenLayers performance\n-   Expose all the advanced _OpenLayers_ features\n-   Try to be as much SSR-friendly as possible (this feature is currently in POC stage, see below)\n-   The current target is _OpenLayers_ 6+\n-   Avoid dependencies when built except for _React_ and OpenLayers (the examples have some dependencies) - currently the single one is [`lru-cache`](https://www.npmjs.com/package/lru-cache) at 8Kbytes\n\n## Long term support of this project\n\nThe birth of this project is related to a huge extortion in the geography community linked to a sexual harassment affair covered up by the French Judiciary. It is maintained as a free service to the geography community so that it can remain as a remainder to a number of companies - including Camptocamp, ESRI, Mapbox and Makina Corpus - that the most noble way to claim size bragging rights is to produce good software. You can safely use this framework in your projects, be assured that it will be maintained very well and for many years to come. It's companion project on the server-side is [`gdal-async`](https://github.com/mmomtchev/node-gdal-async).\n\n## Alternatives\n\n**<- Light-Weight --- Feature-Rich ->**\n\n[pigeon-maps](https://pigeon-maps.js.org/) - [react-leaflet](https://react-leaflet.js.org/) - [rlayers](https://mmomtchev.github.io/rlayers/)\n\nAmong the completely free and open source alternatives for creating maps with _React_, on a scale going from the most light-weight to the most feature-rich solution, _rlayers_ is the right-most one.\n\nIt offers the full power _OpenLayers_ - dynamic reprojections, comprehensive event handlers, a very rich set of supported formats, interfaces and layer types and a very good performance for very complex maps. This comes at the price of a quite significant total bundle size.\n\n## Installation\n\n```bash\nnpm --save install rlayers ol react react-dom\n```\n\n### Compatibility Matrix\n\n_OpenLayers_ and _React_ are peer dependencies and should be installed separately.\n\n_React_ is supported from version 16.8.0.\n\n---\n\n| rlayers          | Unit-tested _OpenLayers_ versions                                              | Unit-tested _React_ versions                |\n| ---------------- | ------------------------------------------------------------------------------ | ------------------------------------------- |\n| 1.0 (_obsolete_) | 6.0                                                                            | 16.8, 16.14, 17.0.2                         |\n| 1.1 (_obsolete_) | 6.6, 6.7, 6.8, 6.9                                                             | 16.8, 16.14, 17.0.2                         |\n| 1.2              | 6.6, 6.7, 6.8, 6.9                                                             | 16.8, 16.14, 17.0.2                         |\n| 1.3              | 6.10, 6.11, 6.12, 6.13, 6.14, 6.14.1                                           | 16.8, 16.14, 17.0.2, 18.0.0                 |\n| 1.4 `(@latest)`  | 6.10, 6.11, 6.12, 6.13, 6.14, 6.14.1, 6.15, 6.15.1, 7.0.0, 7.1.0, 7.2.0, 7.2.2 | 16.8, 16.14, 17.0.2, 18.0.0, 18.1.0, 18.2.0 |\n\n---\n\nWhen using dynamic styles with React 18, you may get a warning in the console in debug mode: https://github.com/mmomtchev/rlayers/issues/40. You can safely ignore it as has no functional consequences - React 18, including the concurrent renderer, is fully supported.\n\n## Usage\n\n_rlayers_ is a set of reusable _React_ components that can be nested in various ways to create map applications for the web through _React_ composition in the true **spirit of _React_**.\nThe components are based on a simplified model of the _OpenLayers_ classes: for example the layers and the sources abstraction levels have been fused into one single level and the map and the view are also represented by a single component.\n\nIn order to avoid confusion between the _OpenLayers_ classes and the _rlayers_ classes which sometimes have the same names - all _rlayers_ classes are prefixed with **R**. If a class begins with **R**, it is from _rlayers_, otherwise it is an _OpenLayers_ class.\n\nThe most important element is the `<RMap>`. Every other element, except `<RStyle>`, requires a parent to function - an `<RLayer>` must be part of a map, an `<RFeature>` must be part of an `<RLayerVector>`, an `<RControl>` must also be part of a map.\n\n### Simple step-by-step example\n\nThis is the simple overlay example - <https://mmomtchev.github.io/rlayers/#/overlays>\n\n```jsx\nimport React from 'react';\nimport {fromLonLat} from 'ol/proj';\nimport {Point} from 'ol/geom';\nimport 'ol/ol.css';\n\nimport {RMap, ROSM, RLayerVector, RFeature, ROverlay, RStyle} from 'rlayers';\nimport locationIcon from './svg/location.svg';\n\n// Create a map, its size is set in the CSS class example-map\n<RMap className='example-map' initial={{center: fromLonLat([2.364, 48.82]), zoom: 11}}>\n    {/* Use an OpenStreetMap background */}\n    <ROSM />\n    {/* Create a single layer for holding vector features */}\n    <RLayerVector zIndex={10}>\n        {/* Create a style for rendering the features */}\n        <RStyle.RStyle>\n            {/* Consisting of a single icon, that is slightly offset\n             * so that its center falls over the center of the feature */}\n            <RStyle.RIcon src={locationIcon} anchor={[0.5, 0.8]} />\n        </RStyle.RStyle>\n        {/* Create a single feature in the vector layer */}\n        <RFeature\n            {/* Its geometry is a point geometry over the monument */}\n            geometry={new Point(fromLonLat([2.295, 48.8737]))}\n            {/* Bind an onClick handler */}\n            onClick={(e) =>\n                {/* e.map is the underlying OpenLayers map - we call getView().fit()\n                to pan/zoom the map over the monument with a small animation */}\n                e.map.getView().fit(e.target.getGeometry().getExtent(), {\n                    duration: 250,\n                    maxZoom: 15\n                })\n            }\n        >\n            {/* The icon is an SVG image that represents the feature on the map\n            while an overlay allows us to add a normal HTML element over the feature */}\n            <ROverlay className='example-overlay'>\n                Arc de Triomphe\n                <br />\n                <em>&#11017; click to zoom</em>\n            </ROverlay>\n        </RFeature>\n    </RLayerVector>\n</RMap>\n```\n\nCheck [examples/static_pages.html](https://github.com/mmomtchev/rlayers/tree/master/examples/static_page.html) for a fully self-contained static HTML page using _rlayers_.\n\nYou can also check the GPLed [XC-DB](https://github.com/mmomtchev/xc-db.git) for a larger and more complex project entirely implemented using React, Redux and rlayers.\n\n#### Contexts\n\nComposition works by using _React_ Contexts. Every nested element uses the context of its nearest parent.\n\nCurrently a context has an `RContextType` and can contain the following elements:\n\n-   `RContext.map` provided by a map, every other element, except an `RStyle` must have a map parent\n-   `RContext.layer` and `RContext.source` provided by all layers - not required for anything at the moment, but can be used to access the underlying _OpenLayers_ objects\n-   `RContext.vectorlayer` and `RContext.vectorsource` provided by vector layers only - required for `<RFeature>`\n-   `RContext.location` and `RContext.feature` provided by a map feature - required for `<ROverlay>` and `<RPopup>`\n-   `RContext.style` provided by a style definition - the only one which can be outside of a map\n\n#### Accessing the underlying _OpenLayers_ objects and API\n\nThe underlying _OpenLayers_ objects can be accessed in a number of different ways:\n\n-   Through the context objects by using `React.Context.Consumer` - [the custom controls example](https://mmomtchev.github.io/rlayers/#/controls) contains an example for using the _OpenLayers_ `map` from `RContext`\n-   In an event handler, when it is a normal function and not an arrow lambda, `this` will contain the _rlayers_ component and `this.context` will contain the context - [the geolocation example](https://mmomtchev.github.io/rlayers/#/geolocation) accesses `this.context.map` to adjust the view\n-   In all event handlers, _OpenLayers_ will pass the target object in `event.target` and the map in `event.map` - [the popups example](https://mmomtchev.github.io/rlayers/#/popups) uses this method\n-   And finally, accessing arbitrary elements, even outside their contexts, is possible by using React references - `React.RefObject`s. [The high performance example](https://mmomtchev.github.io/rlayers/#/igc) contains an example of this. The underlying _OpenLayers_ objects can be accessed through the `ol` property of every component. Additionaly, for `layer` objects, the underlying _OpenLayers_ source can be accessed through `source`:\n    ```ts\n    const layerRef = React.createRef() as React.RefObject<RLayerVector>;\n    ```\n    Then after rendering:\n    ```jsx\n    <RLayerVector ref={layerRef} />\n    ```\n    `layerRef.current.ol` will contain the _OpenLayers_ layer and `layerRef.current.source` will contain the source. This is the only way of accessing the object outside its context.\n\n### Styles\n\nStyle definitions can be placed anywhere inside the DOM and can be referenced with a _React_ reference. rlayers includes two special types for dealing with styles:\n\n-   `RStyleRef` which is an alias to `React.RefObject<RStyle>` is a _React_ reference to an `<RStyle>` element. It can be transparently used everywhere where a classical _OpenLayers_ `StyleLike` is required\n-   `RStyleLike` is the new union type that allows for `StyleLike` or a `RStyleRef`\n\nA style placed inside a vector layer will be automatically applied to that vector layer.\n\nA style can either be static or dynamic. A static style depends only on its properties. A dynamic style is a function that takes an _OpenLayers_ `Feature` object as its input and returns a `Style`. A dynamic style creates a new object for every rendered feature, so this must be taken into account. A simple caching mechanism is also provided, based on a user-supplied hash function. It can greatly improve performance when the majority of the features use relatively few different styles.\n\nYou can refer to\n\n-   <https://mmomtchev.github.io/rlayers/#/features> for a basic example with static styles;\n-   <https://mmomtchev.github.io/rlayers/#/vectortiles> for a more complete example with dynamic styles;\n-   <https://mmomtchev.github.io/rlayers/#/cluster> for the use of caching.\n\nClassical _OpenLayers_ `StyleLike` objects are supported too, but this is not the **_React_ way**. Still, if you need every last bit of performance, writing an optimized _OpenLayers_ style function is the best solution.\n\n### Performance\n\nReact is a wonderful framework that makes it very easy to write complex web applications without having to manually handle all the interdependencies between the various components. This is the reason why it is called _React_ - components automatically _React_ to changes in other components. In the true spirit of _React_, _rlayers_ prefers to err on the safe side - always updating when there is a chance that the component needs updating - making it easy on the beginner who wants simple interface while still allowing the experienced engineer to achieve the performance he needs.\n\nWhen high performance is required, particular care must be taken that the component properties do not change without a reason. This is especially true when the `pointermove` event is used. In these cases one should avoid using anonymous objects, arrays or functions as properties.\n\nTake for example this:\n\n```jsx\n<RFeature\n    geometry={new Point(fromLonLat([2.295, 48.858])}\n    onClick={(e: MapBrowserEvent) => process(e.target)}\n/>\n```\n\nThis is a feature that will be re-evaluated at every frame. Its geometry appears to be a constant, but it is in fact an anonymous object that is created at every frame - even if it always holds the same value. Passing a constant is one way around this, but the true _React way_ is using the two tools _React_ provides: `React.useMemo` and `React.useCallback`. They memoize the value and take care to always return a reference to the same object unless one of the listed dependencies is modified.\n\nThis is a much better performing code that won't rerender the feature component:\n\n```jsx\n<RFeature\n    geometry={React.useMemo(new Point(fromLonLat([2.295, 48.858]), [/* no deps */])}\n    onClick={React.useCallback((e: MapBrowserEvent) => process(e.target), [/* no deps */])}\n/>\n```\n\nAnonymous objects, arrays and **especially lambdas** in the properties of a component are prime candidates for memoization. Sometimes, you can also memoize whole components or groups of components - for a very significant performance boost.\n\nGenerally, if you are binding code to the `pointermove` event and your performance is not good enough, this is the first thing you should be looking at - which components update at every `pointermove` and why.\n\nThese 3 examples run code on various high-frequency events, take a look at them:\n\n-   [Clustering](https://mmomtchev.github.io/rlayers/#/cluster) runs the styling function every time the map is panned or zoomed\n-   [Drop a pin](https://mmomtchev.github.io/rlayers/#/pindrop) runs code on every `pointermove` that carefully avoids rerendering\n-   [Geo data](https://mmomtchev.github.io/rlayers/#/geodata) updates components at every `pointerenter`/`pointerleave`\n-   [The high performance example](https://mmomtchev.github.io/rlayers/#/igc) is a complex example that runs lots of code and updates components at every `pointermove`\n\n## Examples\n\nThe examples can be found here:\n<https://mmomtchev.github.io/rlayers/>\n\n## Next.js\n\nWhen using with Next.js, you have to install `next-transpile-modules`:\n\n```shell\nnpm install --save next-transpile-modules\n```\n\nAnd then create the following `next.config.js`:\n\n```js\nconst withTranspile = require('next-transpile-modules')(['ol', 'rlayers']);\nmodule.exports = withTranspile({experimental: {esmExternals: 'loose'}});\n```\n\nIt is known to work with Next.js 10 to Next.js 13. You can check [`rlayers-npm-tests`](https://github.com/mmomtchev/rlayers-npm-tests) repository for examples for working configurations.\n\n### Server-Side Rendering\n\nServer-side rendering of map components is difficult - there is still no comprehensive solution. Besides the obvious complexities of rendering on canvas outside the browser, one of the major issues is that server-side rendering runs before the browser layout flowing - and thus must work independent of layout and resolution.\n\nThe best solution is to use a WMS-compatible server (such as Geoserver) and to serve prerendered maps over WMS - eventually replacing the initial image by a canvas.\n\nAn intermediate solution, which does not require extensive server-side investment (such as Geoserver), but is limited to static layout(s), is to prerender one (or one per screen size) image to be used as a temporary place-holder until the map is loading. In this case, at least some devices, will get an ugly looking map for the first few seconds.\n\nPushing the initial tiles is also an option:\n\n-   when combined with a WMS-server it could deliver pixel-perfect maps with on the first HTTP request\n-   without a WMS-server it could still avoid doing a large number of HTTP requests on the first load\n\nCurrently, server-side rendering of raster layers on fixed map sizes has reached POC status and an online demo is accessible at https://rlayers-ssr.meteo.guru/.\nThe code can be found in the `ssr` branch of this project. The `next.js` project can be found at <https://github.com/mmomtchev/rlayers-ssr-demo.git>. _This is still not a user-friendly, install-and-run project._ Take a look at `pages/index.js` if you want see how it is meant to be used.\n\nAs of March 2022, SSR support is stale and I am not working on it anymore.\n\n## API\n\nYou can browse the full documentation at <https://mmomtchev.github.io/rlayers/api>.\n\n## License\n\n[ISC](https://choosealicense.com/licenses/isc/)"},"npm":{"downloads":[{"from":"2023-01-07T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":18},{"from":"2023-01-01T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":275},{"from":"2022-12-09T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":1679},{"from":"2022-10-10T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":6336},{"from":"2022-07-12T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":11805},{"from":"2022-01-08T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":20799}],"starsCount":0},"github":{"starsCount":89,"forksCount":12,"subscribersCount":5,"issues":{"count":122,"openCount":9,"distribution":{"3600":41,"10800":9,"32400":15,"97200":25,"291600":9,"874800":5,"2624400":1,"7873200":7,"23619600":5,"70858800":5,"212576400":0},"isDisabled":false},"contributors":[{"username":"mmomtchev","commitsCount":459},{"username":"snyk-bot","commitsCount":23},{"username":"impleri","commitsCount":4},{"username":"clacladev","commitsCount":1}],"commits":[{"from":"2023-01-01T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":2},{"from":"2022-12-09T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":21},{"from":"2022-10-10T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":29},{"from":"2022-07-12T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":49},{"from":"2022-01-08T00:00:00.000Z","to":"2023-01-08T00:00:00.000Z","count":180}],"statuses":[{"context":"codecov/patch","state":"success"},{"context":"codecov/project","state":"success"}]},"source":{"files":{"readmeSize":17846,"testsSize":256449,"hasNpmIgnore":true,"hasChangelog":true},"linters":["eslint","prettier"],"coverage":0.99}},"evaluation":{"quality":{"carefulness":0.9999999999999999,"tests":0.9984999999999999,"health":1,"branding":0.4},"popularity":{"communityInterest":110,"downloadsCount":2112,"downloadsAcceleration":1.8957191780821923,"dependentsCount":0},"maintenance":{"releasesFrequency":1,"commitsFrequency":1,"openIssues":1,"issuesDistribution":0.9}},"score":{"final":0.6861222577514945,"detail":{"quality":0.9975323662454962,"popularity":0.1053596194985088,"maintenance":0.9999619458667646}}}