Skip to content

Commit

Permalink
added region support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbnr committed Mar 25, 2024
1 parent 7ba3d8d commit dd128a5
Show file tree
Hide file tree
Showing 34 changed files with 549 additions and 101 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ For the most flexibility, you can provide a number of configuration and data val
- **`link`:** A URL. If present then clicking on a marker will take the user to the URL provided. You can use this to link to other New Relic pages or your own systems.
- **`tooltip_label_of_your_choice`:** The tooltip that appears when you hover over a marker can display as many values as you require. Simply provide as many 'tooltip_' fields as you require. The label will be automatically created from the text after the "tooltip_" string. Pro tip: Prefix your tooltip label to affect sorting. e.g. "atooltip_zoo_name" will appear above "ztooltip_aardvark" in the tool tip.

### Regions Query
Regions can be rendered as an alternative or in additon to markers. Use the same configuration opttions as above with following changes:

- **`iso_a3` or `iso_a2`:** The ISO A3 or ISO A2 country code (e.g. "GBR" or "GB") (Replaces latitide/longitude)
- **`tooltip_header`**: By default the country name is displayed as tool tip header. You can override by supplying a value here. Specify empty string or NONE to remove the header entirely.


#### Precision, prefix and suffix
Its possible to specify the precision of numbers and add prefix/suffix to values. These adjustments can be made to the `icon_label`` and `tooltip_xxx`` fields by providing extra fields:
- **`_precision`:** Sets the number of decimal places to display. e.g. `select ... 2 as 'icon_label_precision'...`
Expand Down
2 changes: 1 addition & 1 deletion nr1.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"schemaType": "NERDPACK",
"id": "6ed5a801-c5ce-45b2-89f1-6adef1c5bee0",
"id": "69229974-8558-4fe9-bb2b-055fd1f88376",
"displayName": "Location Geo Map Viz",
"description": "Location Geo Map Viz"
}
10 changes: 8 additions & 2 deletions visualizations/store-map-viz/components/LocationPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { Tooltip } from "react-leaflet";

const LocationPopup = ({ location, config }) => {
const LocationPopup = ({ location, config, title, sticky }) => {
const items = config.map((item) => {
return (
<>
Expand All @@ -15,8 +15,14 @@ const LocationPopup = ({ location, config }) => {
);
});

let titleHeader;
if(title && title!="") {
titleHeader=<h4>{title}</h4>
}

return (
<Tooltip>
<Tooltip sticky={sticky}>
{titleHeader}
<div className="popup-grid">{items}</div>
</Tooltip>
);
Expand Down
3 changes: 2 additions & 1 deletion visualizations/store-map-viz/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const MapView = () => {
}
}, [zoom, center]);


//map ratser tiles: https://wiki.openstreetmap.org/wiki/Raster_tile_providers
return (
<Map ref={mapRef} center={center} zoom={zoom} style={mapStyle}>
Expand All @@ -56,7 +57,7 @@ const MapView = () => {
/>
<Markers />
{/* uncomment to turn on Map GeoJson features */}
{/* <Regions /> */}
<Regions />
</Map>
);
};
Expand Down
77 changes: 3 additions & 74 deletions visualizations/store-map-viz/components/Markers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,77 +14,7 @@ import LocationPopup from "./LocationPopup";
import { useProps } from "../context/VizPropsProvider";
import { DEFAULT_DISABLE_CLUSTER_ZOOM, MARKER_COLOURS } from "../constants";

const deriveStatus = (location) => {
const {
threshold_critical: critical,
threshold_warning: warning,
value,
} = location;

if (critical === undefined && warning === undefined) {
location.status = "NONE";
return;
}

const thresholdDirection =
critical !== undefined && warning !== undefined && critical < warning
? "LESS"
: "MORE";

let status = "OK";

if (thresholdDirection === "LESS") {
if (critical !== undefined && value <= critical) {
status = "CRITICAL";
} else if (warning !== undefined && value >= critical && value <= warning) {
status = "WARNING";
}
} else {
if (critical !== undefined && value >= critical) {
status = "CRITICAL";
} else if (warning !== undefined && value < critical && value >= warning) {
status = "WARNING";
}
}

location.status = status;
};

const formatValues = (location) => {
Object.keys(location).forEach((key) => {
const isTooltip = key.includes("tooltip_");
const isIconLabel = key === "icon_label" && !key.includes("_precision");

if (isTooltip || isIconLabel) {
const precisionKey = `${key}_precision`;
const prefixKey = `${key}_prefix`;
const suffixKey = `${key}_suffix`;

if (location[precisionKey] !== undefined) {
try {
location[key] = location[key].toFixed(
parseInt(location[precisionKey])
);
delete location[precisionKey];
} catch (error) {
console.warn(
`Value for ${key} does not appear to be numeric, can't change precision`
);
}
}

if (location[prefixKey] !== undefined) {
location[key] = `${location[prefixKey]}${location[key]}`;
delete location[prefixKey];
}

if (location[suffixKey] !== undefined) {
location[key] = `${location[key]}${location[suffixKey]}`;
delete location[suffixKey];
}
}
});
};
import {deriveStatus, formatValues} from "../utils/dataFormatting";

const Markers = () => {
// const nerdletState = useContext(NerdletStateContext);
Expand Down Expand Up @@ -121,14 +51,14 @@ const Markers = () => {

try {
const response = await NerdGraphQuery.query({ query, variables });
const results = response?.data?.actor?.account?.sales?.results;
const results = response?.data?.actor?.account?.markers?.results;
if (results && Array.isArray(results)) {
results.forEach((location) => {
deriveStatus(location);
formatValues(location);
});
}
setLocations(response?.data?.actor?.account?.sales?.results);
setLocations(response?.data?.actor?.account?.markers?.results);
} catch (error) {
console.error("Error fetching data:", error);
// Handle error appropriately
Expand Down Expand Up @@ -157,7 +87,6 @@ const Markers = () => {

const tooltipConfig = generateTooltipConfig(locations);
if (locations === undefined) {
console.log("No locations in NRQL results to plot. Check the query.");
return null;
}
return (
Expand Down
36 changes: 20 additions & 16 deletions visualizations/store-map-viz/components/Region.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import React from "react";
import { GeoJSON, Tooltip } from "react-leaflet";

const Region = ({ region }) => {
const style = () => ({ color: "white", fillColor: "blue", opacity: 0.5 });

const percentage = "100";
import { GeoJSON, } from "react-leaflet";
import {regionStatusColor } from "../utils/map";
import LocationPopup from "./LocationPopup";

const Region = ({ region, location, tooltipConfig }) => {
const style = () => ({ color: regionStatusColor(location.status).borderColor, fillColor: regionStatusColor(location.status).color, opacity: 0.5 });
let tooltipTitle=region.properties.ADMIN;
if(location.tooltip_header || location?.tooltip_header=="") {
if(location.tooltip_header == "NONE" || location.tooltip_header=="") {
tooltipTitle=null;
} else {
tooltipTitle=location.tooltip_header;
}
}
return (
<GeoJSON data={region} style={style}>
{/* <Tooltip
direction="center"
offset={[0, 0]}
opacity={0.8}
permanent
className="custom-tooltip"
>
{`${percentage}%`}
</Tooltip> */}
<GeoJSON data={region} style={style}
onClick={() => {
if (location.link) {
window.open(location.link, "_blank");
}
}}>
<LocationPopup location={location} config={tooltipConfig} sticky={true} title={tooltipTitle}/>
</GeoJSON>
);
};
Expand Down
101 changes: 96 additions & 5 deletions visualizations/store-map-viz/components/Regions.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,102 @@
import React from "react";
import React, {useEffect, useContext, useState} from "react";
import Region from "./Region";
import allGeoRegions from "../geo/all-geo-regions";
// import allUKRegions from "../geo/uk-regions/all-uk-regions";
import countries from "../geo/countries.geojson.json"
import {PlatformStateContext, NerdGraphQuery} from "nr1";
import { nerdGraphMarkerQuery } from "../queries";
import { FETCH_INTERVAL_DEFAULT } from "../constants";
import {deriveStatus, formatValues} from "../utils/dataFormatting";
import { useProps } from "../context/VizPropsProvider";
import { generateTooltipConfig} from "../utils/map";


const Regions = () => {
return allGeoRegions.map((region, index) => (
<Region key={index} region={region} />
));

const {
accountId,
regionsQuery,
fetchInterval,
ignorePicker,
defaultSince
} = useProps();

const [regions, setRegions] = useState([]);

const defSinceString =
defaultSince === undefined || defaultSince === null
? ""
: " " + defaultSince;
if (regionsQuery === null || regionsQuery === undefined) {
return null;
}


const { timeRange } = useContext(PlatformStateContext);

useEffect(() => {
const fetchData = async () => {
const query = nerdGraphMarkerQuery(
regionsQuery,
timeRange,
defSinceString,
ignorePicker
);
const variables = { id: parseInt(accountId) };

try {
const response = await NerdGraphQuery.query({ query, variables });
const results = response?.data?.actor?.account?.markers?.results;
if (results && Array.isArray(results)) {
results.forEach((location) => {
deriveStatus(location);
formatValues(location);
});
}
setRegions(response?.data?.actor?.account?.markers?.results);
} catch (error) {
console.error("Error fetching data:", error);
// Handle error appropriately
}
};

// Perform the immediate fetch to populate the initial data
fetchData();

// Then set an interval to continue fetching
const fetchIntervalms = (fetchInterval || FETCH_INTERVAL_DEFAULT) * 1000;
const intervalId = setInterval(fetchData, fetchIntervalms);
// Clear the interval when the component unmounts
return () => clearInterval(intervalId);
}, [timeRange, fetchInterval]);

if(!regions || regions.length == 0) {
return null; //no regions to display
} else {
const tooltipConfig=generateTooltipConfig(regions)

let geoFeatureLocations = regions.map((location,index)=>{

let feature;
if(location.iso_a3 && location.isa_a3!="") {
feature=countries.features.find((f)=>{ return f.properties.ISO_A3 == location.iso_a3;});
} else {
if(location.iso_a2 && location.isa_a2!="") {
feature=countries.features.find((f)=>{return f.properties.ISO_A2 == location.iso_a2;})
}
}
if(feature) {
return <Region key={index} region={feature} location={location} tooltipConfig={tooltipConfig}/>;
} else {
console.log("Region could not be found in geo region map",location)
return null;
}
})

return geoFeatureLocations;
}



};

export default Regions;
14 changes: 14 additions & 0 deletions visualizations/store-map-viz/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,29 @@ export const MARKER_COLOURS = {
noneColour: "#0c74df", //grey for group
noneBorder: "#0c74df70",
noneText: "#FFF",
noneRegionColour: "#0c74df",
noneRegionColourBorder: "#0c74df70",

criticalColour: "#DF2E23", // Red color for alert
criticalColourBorder: "#DF2E2370",
criticalColourText: "#fff",
criticalRegionColour: "#DF2E23",
criticalRegionColourBorder: "#DF2E2370",


warningColour: "#FFD23D", // Amber color for warning
warningColourBorder: "#FFD23D70",
warningColourText: "#293238",
warningRegionColour: "#FFD23D",
warningRegionColourBorder: "#FFD23D70",


safeColour: "#05865B", // Green color for safe
safeColourBorder: "#05865B70",
safeColourText: "#FFF",
safeRegionColour: "#05865B",
safeRegionColourBorder: "#05865B70",

groupColour: "#757575", //grey for group
groupBorder: "#75757570", //border, including transparency
groupText: "#fff",
Expand Down
261 changes: 261 additions & 0 deletions visualizations/store-map-viz/geo/countries.geojson.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion visualizations/store-map-viz/geo/info.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Get GEO Data (GEOJSON) from here if needed
https://cartographyvectors.com/geo/united-kingdom
https://cartographyvectors.com/geo/united-kingdom
https://github.com/datasets/geo-countries
31 changes: 31 additions & 0 deletions visualizations/store-map-viz/geo/uk-regions/all-uk-regions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { northEastUK } from "./northeast";
import { northWestUK } from "./northwest";
import { yorkshireAndTheHumber } from "./yorkshireandthehumber";
import { eastMidlands } from "./eastmidlands";
import { westMidlands } from "./westmidlands";
import { eastOfEngland } from "./eastofengland";
import { london } from "./london";
import { southEastUK } from "./southeast";
import { southWestUK } from "./southwest";
import { wales } from "./wales";
import { scotland } from "./scotland";
import { northernIreland } from "./northernireland";
import { ireland } from "./ireland";

const allUKRegions = [
northEastUK,
northWestUK,
yorkshireAndTheHumber,
eastMidlands,
westMidlands,
eastOfEngland,
london,
southEastUK,
southWestUK,
wales,
scotland,
northernIreland,
ireland,
];

export default allUKRegions;
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit dd128a5

Please sign in to comment.