diff --git a/docs/api-reference/color/_category_.json b/docs/api-reference/color/_category_.json new file mode 100644 index 0000000..5cae2dd --- /dev/null +++ b/docs/api-reference/color/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Color", + "position": 3 +} diff --git a/docs/api-reference/color/color.md b/docs/api-reference/color/color.md new file mode 100644 index 0000000..ddd0b07 --- /dev/null +++ b/docs/api-reference/color/color.md @@ -0,0 +1,39 @@ +--- +sidebar_position: 1 +sidebar_label: Overview +slug: overview +--- + +# Working with colors on floor plans + +One of the core usage for Smplrspace is data visualization. As such, good utilization of color theory is an advantage to create a great user experience. With the `Color` class, we wrap the knowledge we collected through years of building visualizations to help you get going quickly. + +## A little context + +A fundamental in our approach, not with visualization at large, but specifically with visualization **on a floor plan**, is that you're doing spatial data visualization. This opens the door to bringing in knowledge from the well researched areas of cartography and geospatial data visualization. One of the most respected names in the industry is [Dr. Cynthia Brewer](https://en.wikipedia.org/wiki/Cynthia_Brewer), American cartographer, who studied the psychological perception of colors used in infographics on maps, and more specifically on [choropleth maps](https://en.wikipedia.org/wiki/Choropleth_map). Her online tool, [ColorBrewer](https://colorbrewer2.org) is where we extracted the great majority of the color scales we suggest here. + +Another aspect of color utilization with Smplrspace, is that you're manipulating colors in Javascript. Having at hand tools that let you programmatically manipulate colors easily, and in a way that is aligned with common data visualization patterns, is a strong advantage. We highly recommend using [chroma.js](https://gka.github.io/chroma.js/) by Gregor Aisch ([homepage](https://driven-by-data.net/)), a former graphics editor at The New York Times ([work](https://www.nytimes.com/by/gregor-aisch)) and co-founder of [Datawrapper](https://www.datawrapper.de/), an online tool to create charts, maps, and tables. Chroma.js is the underlying color utility used in the Smplrspace codebase and our color scales come loaded with options powered directly by the library. + +## Color tools we provide + +### Scales and legend + +The `Color` class in smplr.js provides a few color tools to help you get going quickly. The main part is color scales. We provide numerical scales based of [ColorBrewer](https://colorbrewer2.org), including sequential single hue, sequential multi-hue, and diverging scales. We also provide a categorical scale that helps you quickly map categories to colors, and a RAG (red/amber/green) scale. + +We also have a `Legend` component suitable for numerical scale, which works with our scales and your custom scales as well. This component is available for React, as well as vanilla Javascript. + +### Color mapping + +Finally, as the Smplrspace viewer is a 3D scene, colors are affected by a number of parameters like lighting, materials, shadows, etc. So colors in the viewer do not behave like clean CSS colors. You can actually see how a single sphere does not have the same color on its whole surface. + +![sphere color](/img/api-reference/sphere-color.png) + +To make colors more predictable, we provide two utility methods to convert colors between their CSS value and their **approximate perceived** value in the viewer. As explained, their is no clear one to one mapping, but we generally found that this works quite well. You could for example use this to render a custom legend for categorical scales. + +### Next pages + +Explore these tools on their respective API reference pages: + +- [Color scales](./scales) +- [Legend](./legend) +- [Utilities](./utils) diff --git a/docs/api-reference/color/legend.md b/docs/api-reference/color/legend.md new file mode 100644 index 0000000..68d52c6 --- /dev/null +++ b/docs/api-reference/color/legend.md @@ -0,0 +1,102 @@ +--- +sidebar_position: 3 +sidebar_label: Legend +slug: legend +--- + +import Legend from '@site/src/components/Legend'; + +# Legend + +## React component + +To render the legend of a numerical color scale, you can use the `` component as follow: + +```tsx +interface LegendProps { + colorScale: (n: number | null | undefined) => string + domain?: [number, number] + ticks?: Record + barStyle?: CSSProperties + labelStyle?: CSSProperties + correctColor?: boolean +} + + +// example usage with Color from smplr +
+ +
+``` + +- `colorScale` is the numerical color scale for which the legend is rendered. It can come from our [`numericScale`](./scales#numerical-scales), or be a custom function that takes a numerical value and returns a color string. +- `domain` - _optional_ - is the range of values to render the scale for. _Default value: `[0,1]`._ +- `ticks` - _optional_ - are the values to label at the bottom of the legend. It is defined as an object where the keys are the numerical values where the tick should be and the value are the labels. By default, there is one tick at each end of the legend with the numerical value displayed without formatting. +- `barStyle` - _optional_ - react style object that will be applied to the bar containing the colors. +- `labelStyle` - _optional_ - react style object that will be applied to the container of the labels. It is merged with ours: `{ fontSize: '0.8em', opacity: 0.5, height: 18 }`. We set the height manually as the labels are absolutely positioned. You may need to change the height if you change the font or its size. +- `correctColor` - _optional_ - lets you choose if the colors of the legend should be corrected to match the ones from the viewer as per the explanation in the [color mapping section](./overview#color-mapping). We correct them by default. _Default value: true._ + +Note that the Legend will fill the width of its container. [Get in touch](mailto:support@smplrspace.com) if you have ideas on improvements or pragmatic options we may have missed. + +## Vanilla javascript + +For non-react codebases, we provide a vanilla Javascript function that wraps this component and renders it into a container. + +```ts +smplr.Color.drawLegend({ + containerId: string + ...legendProps: LegendProps // see above +}) + +// example usage +smplr.Color.drawLegend({ + containerId: 'smplr-legend', + colorScale: Color.numericScale({ + name: Color.NumericScale.RdYlBu, + domain: [10, 30], + }), + domain: [10, 30], + ticks: { + 10: '10°C', + 20: '20°C', + 30: '30°C', + }, +}) +``` + +- `containerId` is the "id" of the html container where smplr.js should render the legend, something like "smplr-legend" that can be found in your html. Only ids are supported, not classes. +- `...legendProps` represents all other options as per the [react component section](#react-component). + +## Example legend + + + +was rendered with the code below: + +```ts +smplr.Color.drawLegend({ + containerId: "legend", + colorScale: smplr.Color.numericScale({ + name: smplr.Color.NumericScale.RdYlBu, + domain: [10, 30], + invert: true, + }), + domain: [10, 30], + ticks: { + 10: "10°C", + 20: "20°C", + 30: "30°C", + }, +}); +``` diff --git a/docs/api-reference/color/scales.md b/docs/api-reference/color/scales.md new file mode 100644 index 0000000..d3fb22f --- /dev/null +++ b/docs/api-reference/color/scales.md @@ -0,0 +1,116 @@ +--- +sidebar_position: 2 +sidebar_label: Scales +slug: scales +--- + +# Color scales + +## Numerical scales + +### numericScale + +This function lets you quickly create scales that map numerical values to colors, and comes with a number of [best practice](./overview#a-little-context) scales, as well as options to customize them. + +```ts +smplr.Color.numericScale({ + name: NumericScale | string + domain?: [number, number] + invert?: boolean + padding?: number | [number, number] + gamma?: number + brighten?: number + saturate?: number + nodata?: string + classes?: number | number[] +}) => ((n: number | null | undefined) => string) + +// example +smplr.Color.numericScale({ + name: smplr.Color.NumericScale.RdYlBu, + domain: [10, 30], + invert: true, +}) +``` + +- `name` is the identifier of the scale. See below for the list of available scales. You can either pass it as a string - `'OrRd'`, or using our provided enum for typesafety and autocompletion - `smplr.Color.NumericScale.OrRd`. +- `domain` - _optional_ - is typically the range of values that will be passed to the scale. It is an array of 2 numbers, where the first number is mapped to the start of the scale, and the second number is mapped to the end of the scale. Values lower than the domain will be mapped to the start of the scale, and values higher to the end. _Default value: `[0,1]`._ +- `invert` - _optional_ - is a boolean used to invert the scale, or swap its start and end. _Default value: false._ +- `padding` - _optional_ - reduces the color range by cutting off a fraction of the scale. A single number applies the same padding to both ends, while an array lets you pad differently on the start and the end of the scale. Negative values can be used to compress the scale and extend its extreme values. _Default value: 0._ +- `gamma` - _optional_ - is used to "shift" a scale's center more the the beginning (gamma < 1) or end (gamma > 1). _Default value: 1._ +- `brighten` - _optional_ - is used to change the lightness of the scale. Positive values brightens it, while negative values darkens it. _Default value: 0._ +- `saturate` - _optional_ - is used to change the saturation of the scale. Positive values saturates it, while negative values desaturates it. _Default value: 0._ +- `nodata` - _optional_ - is the color used when the value passed to the scale is not valid. _Default value: #6a6c6c._ +- `classes` - _optional_ - is used to return a distinct set of colors instead of a continuous scale. A number generates a scale broken into equi-distant classes, while an array lets you choose the "breakpoints" to use for the classes. + +The scales available are: + +- single hue sequential scales: Oranges, Reds, Greens, Purples, Greys, Blues - from [ColorBrewer](https://colorbrewer2.org). +- multi-hue sequential scales: OrRd, PuBu, BuPu, BuGn, YlOrBr, YlGn, RdPu, YlGnBu, GnBu, YlOrRd, PuRd, PuBuGn – from [ColorBrewer](https://colorbrewer2.org), and Viridi initially from [Matplotlib](https://bids.github.io/colormap/). +- diverging scales: Spectral, RdYlGn, RdBu, PiYG, PRGn, RdYlBu, BrBG, RdGy, PuOr - from [ColorBrewer](https://colorbrewer2.org). + +The [air quality](/examples/air-quality) example uses `numericScale` and can be used as a code playground to test out the options. You can also chose and configure a color scale using our [colors playground](https://colors.smplrspace.io). + +:::tip + +Configure a numerical scale using our interactive [colors playground](https://colors.smplrspace.io) + +::: + +## Categorical scales + +### categoryScale + +This function lets you quickly map a discrete number of named categories to colors. It comes with fallback color built-in, as well as type safe categories. + +```ts +smplr.Color.numericScale({ + categories: Record + nodata?: string +}): ((category: C) => string) + +// example +smplr.Color.categoryScale({ + categories: { + sunny: 'yellow', + rainy: 'blue', + cloudy: 'grey' + }, +}) +``` + +- `categories` is an object matching category names to colors. +- `nodata` - _optional_ - is the color used when the value passed to the scale is not a known category. _Default value: #6a6c6c._ + +Typescript tip: You may disable category typesafety by passing a "loose" type hint to the function: `categoryScale({...})`. + +### ragScale + +This function is built on top of `categoryScale` and provide a shortcut for red/amber/green scenarios. Here, the scale comes with pre-defined and optimized RAG colors, that can be customized. And the values "red", "amber", and "green" are always valid for quick testing. + +```ts +smplr.Color.ragScale({ + categories?: Partial> + colors?: { + red?: string + amber?: string + green?: string + } + nodata?: string +}): ((category: C) => string) + +// example +smplr.Color.ragScale({ + categories: { + red: 'vacant', + amber: 'expiring', + green: 'occupied', + }, +}) +``` + +- `categories` - _optional_ - is an object matching category names to the RAG classes. _Default value: `{ red: 'red', amber: 'amber', green: 'green' }`._ +- `colors` - _optional_ - is an object used to provide custom RAG colors. _Default value: `{ red: '#ff3f34', amber: '#c77a15', green: '#3aa655' }`._ +- `nodata` - _optional_ - is the color used when the value passed to the scale is not a known category. _Default value: #6a6c6c._ + +Typescript tip: You may disable category typesafety by passing a "loose" type hint to the function: `ragScale({...})`. diff --git a/docs/api-reference/color/utils.md b/docs/api-reference/color/utils.md new file mode 100644 index 0000000..6e7de98 --- /dev/null +++ b/docs/api-reference/color/utils.md @@ -0,0 +1,25 @@ +--- +sidebar_position: 4 +sidebar_label: Utilities +slug: utils +--- + +# Color utilities + +## cssToSmplrColor + +As explained in the [color mapping section](./overview#color-mapping), colors in the viewer are matching one to one to their CSS value. To convert a CSS color into its **approximate perceived** value in the viewer, you can call this function: + +```ts +smplr.Color.cssToSmplrColor(c: string) => string +``` + +where `c` is the color string in CSS. Accepted formats are the hexadecimal value (e.g. "#2393d4") or the name of the color (e.g. "pink"). + +## smplrToCssColor + +This is the opposite function of `cssToSmplrColor` and converts a color value as it is **approximately perceived** in the viewer, into its CSS equivalent. + +```ts +smplr.Color.smplrToCssColor(c: string) => string +``` diff --git a/docs/api-reference/space/data-layers.md b/docs/api-reference/space/data-layers.md index 5f1eac1..55268d6 100644 --- a/docs/api-reference/space/data-layers.md +++ b/docs/api-reference/space/data-layers.md @@ -8,7 +8,7 @@ The introduction to data layers and how to add, update and remove them is in the ## Generic options -Some options correspond to generic behaviours that are shared by all data layer types, making it easy to swap between similar layer types (e.g. "point" and "icon"). +Some options correspond to generic behaviours that are shared by all interactive data layers, making it easy to swap between similar layer types (e.g. "point" and "icon"). ```ts space.addDataLayer({ @@ -38,6 +38,7 @@ A point layer has each data element rendered as a sphere. space.addDataLayer({ id: string, type: 'point', + shape: 'sphere' | 'cube', data: [{ id: string | number, position: { @@ -48,23 +49,42 @@ space.addDataLayer({ }, ...customData: object }], - diameter?: number | (dataElement: object) => number, - anchor?: 'bottom' | 'center' | 'top', color?: string | (dataElement: object) => string, + anchor?: 'bottom' | 'center' | 'top', alpha?: number, onDrag?: ({ data: object }) => void, onDrop?: ({ data: object, position: object }) => void + // sphere shape options + diameter?: number | (dataElement: object) => number | { x: number; y: number; z: number }, + // cube shape options + size?: number + width?: number + height?: number + depth?: number + scale?: (dataElement: object) => number | { x: number; y: number; z: number }, }) => DataLayerController ``` - `id` is a unique identifier for this layer which is used for updates. +- `shape` is the the 3D shape used to render each data element. Each shape comes with its own options defined below. - `data` is an array of objects (refered to as data elements) to be rendered. Each element **must** have an `id` (unique identifier within the data array) and a `position`. Elements can also contain any additional custom data used for rendering options. -- `diameter` - _optional_ - defines the diameter of the sphere to render in meters. It can be defined as a number for all elements or per element with a function that takes each element as argument and returns the diameter for that element. _Default value: 1m._ -- `anchor` - _optional_ - defines if the position provided for each data element corresponds to the bottom, center or top of the sphere. _Default value: center._ - `color` - _optional_ - defines the color of the sphere to render. It can be defined as a hexadecimal string like "#3a3c3c" for all elements or per element with a function that takes each element as argument and returns the hexadecimal color string for that element. _Default value: "#2393d4"_ +- `anchor` - _optional_ - defines if the position provided for each data element corresponds to the bottom, center or top of the sphere. _Default value: center._ - `alpha` - _optional_ - defines the transparency of the spheres for the whole layer. Element specific alpha value is not supported. The value should be between 0 (invisible) and 1 (opaque). _Default value: 1_ - `onDrag, onDrop` - _optional_ - providing either or both handlers will make data elements of the layer draggable. Each handler takes the dragged data element as argument. `onDrop` also receives the new position of the element so it can be updated in your app state and database. +##### Sphere shape options + +- `diameter` - _optional_ - defines the diameter of the sphere to render in meters. It can be defined as a number for all elements or per element with a function that takes each element as argument and returns the diameter for that element. The diameter can be a number to render a perfectly round sphere, or an object providing the "diameter" per axis to render ellipsoids. _Default value: 1m._ + +##### Cube shape options + +- `size` - _optional_ - defines the default size of each side of the cube in meters. _Default value: 1m._ +- `width` - _optional_ - defines the width of the cube in meters. _Default value: same as size._ +- `height` - _optional_ - defines the height of the cube in meters. _Default value: same as size._ +- `depth` - _optional_ - defines the depth of the cube in meters. _Default value: same as size._ +- `scale` - _optional_ - defines the per-data-element multiplication factor to the size of the cubes. It is a function that takes each element as argument and returns the scale factor for that element. The scale factor can be a number for uniform scaling in all directions, or an object providing one factor per axis. + The [internet of things](/examples/iot) example provides code implementation of point data layers. The [add data elements](/examples/add-data-elements) example gives a full overview of draggable layers. ### Icon layer @@ -212,10 +232,13 @@ space.addDataLayer({ anchor?: 'bottom' | 'center' | 'top', color?: string | (dataElement: object) => string, alpha?: number, - animation?: false | 'railway' | 'waves', + animation?: false | 'waves' | 'railway', + // waves animation options speed?: number, amplitude?: number, - waves?: number + waves?: number, + // railway animation options + speed?: number, }) => DataLayerController ``` @@ -227,11 +250,18 @@ space.addDataLayer({ - `color` - _optional_ - defines the color of the sphere to render. It can be defined as a hexadecimal string like "#3a3c3c" for all elements or per element with a function that takes each element as argument and returns the hexadecimal color string for that element. _Default value: "#2393d4"._ - `alpha` - _optional_ - defines the transparency of the spheres for the whole layer. Element specific alpha value is not supported. The value should be between 0 (invisible) and 1 (opaque). _Default value: 1._ - `animation` - _optional_ - use `false` to disable animation, `'railway'` to move spheres in a queue like wagons, or `'waves'` to scale spheres like a wave. _Default value: false._ -- `speed` - _optional_ - defines the speed of the animation. In 'railway' mode, speed 1 means each sphere gets to next one in 1 second. In 'waves' mode, speed 1 means it takes 1 second for a wave to go up and down for each sphere. _Default value: 1._ -- `amplitude` - _optional, 'waves' mode only_ - defines the scaling factor of the waves, so 0.4 means each sphere will grow 40% of its diameter. _Default value: 0.4._ -- `waves` - _optional, 'waves' mode only_ - defines the number of waves visible on each line at a single point of time. _Default value: 1._ -Live example coming soon. +##### Waves animation options + +- `speed` - _optional_ - defines the speed of the animation. Speed 1 means it takes 1 second for a wave to go up and down for each sphere. _Default value: 1._ +- `amplitude` - _optional_ - defines the scaling factor of the waves, so 0.4 means each sphere will grow 40% of its diameter. _Default value: 0.4._ +- `waves` - _optional_ - defines the number of waves visible on each line at a single point of time. _Default value: 1._ + +##### Railway animation options + +- `speed` - _optional_ - defines the speed of the animation. Speed 1 means each sphere gets to next one in 1 second. _Default value: 1._ + +Live code example coming soon. ### Furniture layer @@ -256,6 +286,81 @@ space.addDataLayer({ The [space booking](/examples/space-booking) example provides a simple implementation of a furniture data layer. +### Heat map layer + +A heat map layer renders a grid of colored "elements" representing the interpolated value of a given metric across the space, based on a few data points for that metric located in the space. The rendered size of each grid "element" communicates the confidence in the interpolated value. It is typically used to display environmental data that has some level of spatial continuity like temperature, air quality, or to a certain extent crowd density. + +**Please take note:** Heat map layers are non-interactive layers. [Generic options](#generic-options) do not apply. + +```ts +space.addDataLayer({ + id: string, + type: 'heatmap', + style: 'spheres' | 'grid' | 'bar-chart', + data: [{ + id: string | number, + position: { + levelIndex: number, + x: number, + z: number, + }, + ...customData: object + }], + value: (dataElement: object) => number, + color: (interpolatedValue: number) => string, + gridSize?: number, + gridFill?: number, + alpha?: number, + mask?: [{ + levelIndex: number, + x: number, + z: number, + }] | Record, + confidenceRadius?: number, + // spheres style options + elevation?: number, + squishFactor?: number, + // grid style options + elevation?: number, + thickness?: number, + // bar-chart style options + height: (interpolatedValue: number) => number +}) => DataLayerController +``` + +- `id` is a unique identifier for this layer which is used for updates. +- `style` lets you choose between multiple rendering styles for the heat map. Each style comes with its own options defined below. +- `data` is an array of objects (refered to as data points) used as base for the value interpolation. Each data point **must** have an `id` (unique identifier within the data array) and a `position` provided in the 2D plane. Data points must contain the value to be used for interpolation, or the data required to compute that value. They can also contain any additional custom data. +- `value` is a function that takes each data point as argument and returns the value for that data point to be used in the interpolation of the heat map values. +- `color` defines the displayed color of the heat map. It is a function that takes an interpolated value as argument and returns the hexadecimal color string used to render the grid "element" with that value. +- `gridSize` - _optional_ - defines the size in meters of each "cell" of the heat map grid. _Default value: 1m._ +- `gridFill` - _optional_ - defines the size of each grid "element" relatively to its "cell". A value of 1 means the element fills up the cell, 0.9 would add a 10% padding, while 1.1 would add a 10% overflow. _Default value: 1._ +- `alpha` - _optional_ - defines the transparency of the rendered grid "elements". The value should be between 0 (invisible) and 1 (opaque). _Default value: 1._ +- `mask` - _optional_ - a 2D polygon coordinates array that lets you define the area where the heat map should be interpolated and rendered. By default, the space's footprint on the active level will be used as mask. You can also pass an object with a mask for each level, with the key being the levelIndex, and for levels with no mask, it will use the level's footprint. +- `confidenceRadius` - _optional_ - defines the distance in meters from the provided data points where interpolation makes sense. Grid "elements" are rendered at their nominal size (see `gridSize` and `gridFill`) when they are in close proximity to a datapoint. As they get further, their rendered size decreases (linearly to the distance to the nearest data point) as a way to communicate the confidence in the interpolated value. When a grid "element"'s distance to the nearest datapoint reaches the confidenceRadius value, it's rendered size reaches 0. By default, the confidenceRadius value is equal to the median of the distance between each data point and its 2 nearest datapoints. + +##### Spheres style options + +- `elevation` - _optional_ - is the height in meters from the active level's ground where the grid "elements" should be rendered. _Default value: 3m._ +- `squishFactor` - _optional_ - lets you deform the spheres in the vertical axis. A value of 0 gives you a perfectly rounded sphere, 0.3 an M&M's type pill, 0.99 a flat sphere, and -2 an elongated ellipsoid. _Default value: 0._ + +##### Grid style options + +- `elevation` - _optional_ - is the height in meters from the active level's ground where the grid "elements" should be rendered. _Default value: 3m._ +- `thickness` - _optional_ - defines the height in meters of each cube making up the grid. _Default value: 0.03m._ + +You can for example set elevation to 0 and thickness to 3 to get a solid grid from the ground to the ceiling (assuming a 3m wall height). + +##### Bar chart style options + +- `height` defines the height of each bar from the ground to the top. It is a function that takes an interpolated value as argument and returns the height in meters of the bar representing an element with that value. + +The [air quality](/examples/air-quality) example uses a heat map layer and can be used as a code playground to test out the options. The [timeheat demo](https://timeheat.smplrspace.io/) showcases the capabilies of the heat map layer and provides a UI-based playground to test out options. + ## Data layer controller A controller for a given data layer is returned when you call `addDataLayer` as [documented here](./overview#add-a-layer). diff --git a/package.json b/package.json index 9e44469..74902d6 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@mantine/core": "^3.0.5", "@mantine/hooks": "^3.0.5", "@mdx-js/react": "^1.6.21", + "@smplrspace/smplr-loader": "^2.14.2-beta.4", "@stackblitz/sdk": "^1.8.2", "@svgr/webpack": "^5.5.0", "chance": "^1.1.8", @@ -46,6 +47,7 @@ "url-loader": "^4.1.1" }, "devDependencies": { + "@types/react": "^18.2.62", "babel-eslint": "^10.1.0", "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", diff --git a/src/components/Legend.tsx b/src/components/Legend.tsx new file mode 100644 index 0000000..e763364 --- /dev/null +++ b/src/components/Legend.tsx @@ -0,0 +1,32 @@ +import React, { useEffect, FC } from 'react' +import { loadSmplrJs } from '@smplrspace/smplr-loader' + +const Legend: FC = () => { + useEffect(() => { + loadSmplrJs('umd', 'dev') + .then((smplr) => { + smplr.Color.drawLegend({ containerId: 'legend', + colorScale: smplr.Color.numericScale({ + name: smplr.Color.NumericScale.RdYlBu, + domain: [10, 30], + invert: true + }), + domain: [10, 30], + ticks: { + 10: '10°C', + 20: '20°C', + 30: '30°C' + } + }) + }) + .catch((error) => console.error(error)) + }, []) + + return ( +
+
Loading...
+
+ ) +} + +export default Legend diff --git a/src/pages/examples/add-data-elements/AddDataElements.js b/src/pages/examples/add-data-elements/AddDataElements.js index a3a5a58..6a45b65 100644 --- a/src/pages/examples/add-data-elements/AddDataElements.js +++ b/src/pages/examples/add-data-elements/AddDataElements.js @@ -204,6 +204,7 @@ const AddDataElements = () => { space.addDataLayer({ id: 'points', type: 'point', + shape: 'sphere', data: autoElevation(points), diameter: 0.5, anchor: 'bottom', diff --git a/src/pages/examples/air-quality/card.png b/src/pages/examples/air-quality/card.png new file mode 100644 index 0000000..6b244d0 Binary files /dev/null and b/src/pages/examples/air-quality/card.png differ diff --git a/src/pages/examples/air-quality/index.js b/src/pages/examples/air-quality/index.js new file mode 100644 index 0000000..adbfbfa --- /dev/null +++ b/src/pages/examples/air-quality/index.js @@ -0,0 +1,26 @@ +/* eslint-disable import/no-webpack-loader-syntax */ +import React from 'react' + +import StackblitzProject from '../../../components/StackblitzProject' + +import { USE_CASES } from '../_categories' + +export const airQuality = { + slug: 'air-quality', + title: 'Air quality', + category: USE_CASES, + description: `Showcase environmental data such as air quality, temperature, and more using our customizable heat map layer.`, + published: true, + stackblitzProjects: [ + { + lang: 'React', + id: 'smplr-heat-map', + openFile: 'SpaceViewer.tsx', + default: true + } + ] +} + +export default function () { + return +} diff --git a/src/pages/examples/index.js b/src/pages/examples/index.js index 8a062de..cbe4cec 100644 --- a/src/pages/examples/index.js +++ b/src/pages/examples/index.js @@ -33,11 +33,13 @@ import { propertyManagement } from './property-management' import { controlledCamera } from './controlled-camera' import { seeThroughWalls } from './see-through-walls' import { customTooltips } from './custom-tooltips' +import { airQuality } from './air-quality' const projects = [ helloWorld, spaceBooking, leasingTenancy, + airQuality, iot, propertyManagement, addDataElements, diff --git a/src/pages/examples/property-management/PropertyManagement.js b/src/pages/examples/property-management/PropertyManagement.js index 0157adc..fcc8c2c 100644 --- a/src/pages/examples/property-management/PropertyManagement.js +++ b/src/pages/examples/property-management/PropertyManagement.js @@ -103,6 +103,7 @@ const PropertyManagement = () => { space.addDataLayer({ id: 'reports', type: 'point', + shape: 'sphere', diameter: 0.6, data: autoElevation(allReports), tooltip: d => d.title, diff --git a/static/img/api-reference/sphere-color.png b/static/img/api-reference/sphere-color.png new file mode 100644 index 0000000..8ac762d Binary files /dev/null and b/static/img/api-reference/sphere-color.png differ diff --git a/yarn.lock b/yarn.lock index cfaaa0f..1219dcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1765,6 +1765,11 @@ url "^0.11.0" webpack-sources "^1.4.3" +"@smplrspace/smplr-loader@^2.14.2-beta.4": + version "2.14.2-beta.4" + resolved "https://registry.yarnpkg.com/@smplrspace/smplr-loader/-/smplr-loader-2.14.2-beta.4.tgz#75aa1e24a4478777c708bb7e0d9e4491a2eca9ad" + integrity sha512-xxr8mXUTWNuLdvarqFzesf8IDp6UB8UhsakmeGKWqaHTEIdxEDlWLs22TX1w+Tgh7qL+z6NLXsLquiBcOaXCtg== + "@stackblitz/sdk@^1.8.2": version "1.8.2" resolved "https://registry.yarnpkg.com/@stackblitz/sdk/-/sdk-1.8.2.tgz#f91c7b35cd5c4523fcacb74b51cee4ec92b608e0" @@ -1983,11 +1988,25 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/prop-types@*": + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + "@types/q@^1.5.1": version "1.5.5" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== +"@types/react@^18.2.62": + version "18.2.62" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.62.tgz#2527a7a54749b1a99c87a4aa8b83e26846face38" + integrity sha512-l3f57BbaEKP0xcFzf+5qRG8/PXykZiuVM6eEoPtqBPCp6dxO3HhDkLIgIyXPhPKNAeXn3KO2pEaNgzaEo/asaw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/sax@^1.2.1": version "1.2.3" resolved "https://registry.yarnpkg.com/@types/sax/-/sax-1.2.3.tgz#b630ac1403ebd7812e0bf9a10de9bf5077afb348" @@ -1995,6 +2014,11 @@ dependencies: "@types/node" "*" +"@types/scheduler@*": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"