Skip to content

Instantly share code, notes, and snippets.

@csprance
Created May 22, 2019 15:52
Show Gist options
  • Save csprance/da865f45c0ea9c4b768d76079d38aa8b to your computer and use it in GitHub Desktop.
Save csprance/da865f45c0ea9c4b768d76079d38aa8b to your computer and use it in GitHub Desktop.
More Map Data Functions in FlowType Javascript
// @flow
/**
* Name: mapUtils
* Created by chris on 4/22/2017.
* Description:
*/
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import PlaceIcon from 'material-ui/svg-icons/maps/place';
import _ from 'lodash';
import * as CONSTANTS from '../../constants/magicNumbers';
import defaultMarkers from '../../constants/default_markers';
import defaultShapes from '../../constants/default_shapes_queries';
import defaultLines from '../../constants/default_lines_queries';
import DivIcon from './components/DivIcon';
import * as L from 'leaflet';
import { getRandomColor } from '../../utils/utils';
import type { Shape, ShapesLayer } from './mapState';
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// Shapes
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
/**
* Converts a database response from getDataFromDb returns an array of ShapeLayer(s)
*/
export function convertDataToShapesLayer(
data: Array<{ json_points: string }>, // the data array response from the database.
query: string // The query used to fetch the data
): ShapesLayer {
return [
{
name: getShapeNameFromQuery(query),
color: getShapeColorFromQuery(query),
shapes: createShapesFromData(data)
}
];
}
/**
* Get the nice name of a query
* @param {string} query - The sqlite3 query
* @returns {string} The nicely formatted name fo the query or the query
*/
export function getShapeNameFromQuery(query: string): string {
// First see if we have a nice name for it based on our default Markers
const matching = defaultShapes.filter(shape => shape.query === query);
// return that otherwise return silver
return matching.length > 0 ? matching[0].name : query;
}
/**
* Get the color of a query from a shape
* @param {string} query - The sqlite3 query
* @returns {string} The nicely formatted name fo the query or the query
*/
export function getShapeColorFromQuery(query: string): string {
const color = defaultShapes.filter(shape => shape.query === query)[0].color();
return color !== undefined ? color : 'red';
}
/**
* Given a query do some magic to determine if we should
* be making a a SHAPE
* @param {string} query - the query to determine if it is a SHAPE
* @returns {Boolean} is it a SHAPE?
*/
export function isShapeQuery(query: string): boolean {
return _.includes(_.mapValues(defaultShapes, 'query'), query);
}
/**
* Creates shapes from json_points string and returns it for use
*/
export function createShapesFromData(
data: Array<{ json_points: string }>
): Array<Shape> {
// for each row return an array of the json points parsed// and converted to lat long
// in order of the keys of the object 1,2,3
return data
.filter(row => row.json_points !== undefined) // filter out any rows we don't need
.map(row => {
// get the keys, sort, and then iterate through and convert to lat long
const jsonPoints = JSON.parse(row.json_points);
return _.keys(jsonPoints)
.map(key => Number(key))
.sort((a, b) => a - b)
.map(index => {
const { lat, lng } = splitXYZToLatLng(jsonPoints[index]);
return [lat, lng];
});
});
}
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// Lines
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
/**
* Converts a database response from getDataFromDb to an array of Line layers
* and returns it for use
* @param {Array} data - the data array response from the database.
* @param {string} query - The query used to fetch the data
* @returns {Array} an array of shape layer(s) [{name: '', shapes: [{}] }]
*/
export function convertDataToLineLayer(
data: Array<any>,
query: string
): Array<{ name: string, lines: Array<any>, color: string }> {
return [
{
name: getLineNameFromQuery(query),
color: getLineColorFromQuery(query),
lines: createLinesFromData(data)
}
];
}
/**
* Get the color of a query from a line
* @param {string} query - The sqlite3 query
* @returns {string} The nicely formatted name fo the query or the query
*/
export function getLineColorFromQuery(query: string): string {
const color = defaultLines.filter(line => line.query === query)[0].color();
return color !== undefined ? color : 'red';
}
/**
* Creates shapes from data and returns it for use
* @param {Array} data - The Array containing the database response to transform
* @returns {Array} an Array of Lines
*
*/
export function createLinesFromData(data: Array<any>): Array<any> {
// for each row return an array of the json points parsed// and converted to lat long
// in order of the keys of the object 1,2,3
return data
.filter(row => row.json_points !== undefined) // filter out any rows we don't need
.map(row => {
// get the keys, sort, and then iterate through and convert to lat long
const jsonPoints = JSON.parse(row.json_points);
return _.keys(jsonPoints)
.map(key => Number(key))
.sort((a, b) => a - b)
.map(index => {
const { lat, lng } = splitXYZToLatLng(jsonPoints[index]);
return [lat, lng];
});
});
}
/**
* Get the nice name of a query
* @param {string} query - The sqlite3 query
* @returns {string} The nicely formatted name fo the query or the query
*/
export function getLineNameFromQuery(query: string): string {
// TODO: These are broken
// First see if we have a nice name for it based on our default Markers
const matching = defaultLines.filter(line => line.query === query);
// return that otherwise return silver
return matching.length > 0 ? matching[0].name : query;
}
/**
* Given a query do some magic to determine if we should
* be making a a LINE
* @param {string} query - the query to determine if it is a LINE
* @returns {Boolean} is it a LINE?
*/
export function isLineQuery(query: string): boolean {
return _.includes(_.mapValues(defaultLines, 'query'), query);
}
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// Heatmap
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
/**
* Converts a database response from getDataFromDb to an array of heatmap points
* and returns it for use this is used for big queries that lag with lots of markers
* @param {Array} data - the data array response from the database.
* @returns {Array} an array of points and intensities [ [lat, lng, intensity], [-35, 64, 1] ]
*/
export const convertDataToHeatmapLayer = (
data: Array<any>
): Array<[number, number, number]> =>
data
.map(i => splitXYZToLatLng(i.hasOwnProperty('pos') ? i.pos : '0,0,0'))
.map(i => [i.lat, i.lng, 1]);
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// Markers
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
/**
* Converts a database response from getDataFromDb to an array of Marker layers
* and returns it for use
* @param {Array} data - the data array response from the database.
* @param {string} query - The query used to fetch the data
* @returns {Array} an array of map layer(s) [{name: '', markers: [{}] }]
*/
export function convertDataToMapLayer(
data: Array<any>,
query: string
): Array<{ name: string, markers: Array<any> }> {
const color = getColorFromQuery(query);
return [
{
name: getMarkerNameFromQuery(query),
color: color,
markers: createMarkersFromData(data, getIconFromQuery(query, color))
}
];
}
/**
* Creates map markers from data and filters any markers in the exact same position out
* @param {Array} data - The Array containing the database response to transform
* @param {XML} icon - The React Icon the marker should use
* @returns {Array} an Array of Map markers
* { name: 'Bogus Data', position: { lat: -34, lng: -5 }, icon }
*/
export function createMarkersFromData(data: Array<any>, icon: any): Array<any> {
return _.uniqBy(
data,
e =>
`${splitXYZToLatLng(e.pos).lat}-${splitXYZToLatLng(e.pos).lng}-${e.name}`
)
.filter(i => i.pos !== undefined)
.map(i => ({
position: splitXYZToLatLng(i.pos),
icon,
name: getMarkerName(i)
}));
}
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// General Utils
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
/**
* Given some row data extract a name like item from it
* @param {any} data - The row Data
* @returns {string} The name of the object to display in a popup
*/
export function getMarkerName(data: any): string {
if (data.hasOwnProperty('name')) return data.name;
return 'Default Name';
}
/**
* Splits an x,y,z string '45,34,2' to a {lat, lng} to use in a leaflet map
* uses the leaflet set in window.L
* @param {string} str - The x, y, z string to split.
* @returns {object} The {lat: 34.234234, lng: -34.32343} object
*/
export function splitXYZToLatLng(str: string) {
if (str !== null) {
const { x, y } = splitToVec3(str);
const lat = window.L.unproject(
[x, -y + CONSTANTS.MAP_PROJECTION_SIZE],
CONSTANTS.MAP_PROJECTION_ZOOM
).lat;
const lng = window.L.unproject(
[x, -y + CONSTANTS.MAP_PROJECTION_SIZE],
CONSTANTS.MAP_PROJECTION_ZOOM
).lng;
return { lat, lng };
}
return { lat: -40, lng: 40 };
}
/**
* Fetch an Icon based on a query
* @param {string} query - The Query used to make the request
* @param {string} color - A color to pass through
* @returns {XML} an Icon matching a query or the default Icon
*/
export function getIconFromQuery(
query: string,
color: string = getRandomColor()
): any {
const matching = defaultMarkers.filter(marker => marker.query === query);
return matching.length > 0 ? matching[0].icon : <PlaceIcon color={color} />;
}
/**
* Fetch a name based on a query
* @param {string} query - The Query used to make the request
* @returns {string} an nice name matching the query
*/
export function getMarkerNameFromQuery(query: string): string {
// First see if we have a nice name for it based on our default Markers
const matching = defaultMarkers.filter(marker => marker.query === query);
// return that otherwise just return the name
return matching.length > 0 ? matching[0].name : query;
}
/**
* Fetch a color based on a query
* @param {string} query - The Query used to make the request
* @returns {string} an nice name matching the query
*/
export function getColorFromQuery(query: string): string {
const matchingDefaultMarker = defaultMarkers.filter(marker => marker.query === query)[0];
return matchingDefaultMarker ? matchingDefaultMarker.color : getRandomColor();
}
/**
* Creates an HTML string from a React SvgIcon
* @param {XML} Icon - Icon to use
* @returns {String} the html string of the react element
*/
export const createDivIcon = (Icon: any): string =>
ReactDOMServer.renderToStaticMarkup(<DivIcon>{Icon}</DivIcon>);
/**
* Splits a vec3 string into a vec3 object
* @example "0,0,0" -> {x: 0, y: 0, z: 0}
* @param {string} str - "0,0,0" the string to split.
* @returns {Object} - {x: 50, y: 20, z: 10} Object of x,y,z Numbers
*/
export const splitToVec3 = (
str: string
): { x: number, y: number, z: number } => {
const [x, y, z] = str.split(',').map(i => Number(i));
return { x, y, z };
};
/**
* Creates a tileset URL for the leaflet base layer map
* @param {String} tileSet - the folder name to create the tile string
* @returns {String} - TileSet URL
*/
export const createTileSiteUrl = (tileSet: string): string =>
process.env.NODE_ENV === 'production'
? `../maps/${tileSet}/{z}/{x}/{y}.png`
: `../resources/maps/${tileSet}/{z}/{x}/{y}.png`;
/**
* takes a lat and lng and a map and returns an {x: x, y: y}
* @param {Number} lat - latitude
* @param {Number} lng - longitude
* @param {L.Map} map - the leaflet map to use unproject with
* @returns {Object} {x: x, y: y} - Object containing x and y keys/values
*/
export const convertLatLngToVec2 = (
lat: number,
lng: number,
map: any
): { x: number, y: number } => {
const point = map.project({ lat, lng }, 5);
return { x: point.x, y: 8196 - point.y };
};
/**
* Checks if an array of objects has any positional data in it
* @param {Array} data - The database response object
* @returns {Boolean} true the object has response data false it does not
*/
export function containsPositionalData(data: Array<any>): boolean {
return data[0].pos !== undefined || data[0].json_points !== undefined;
}
/**
* Checks if an array of objects has any shape data in it
* this means it has a column name json_points in it
* @param {Array} data - The database response object
* @returns {Boolean} true the object has response data false it does not
*/
export function containsShapeData(data: Array<any>): boolean {
return data[0].json_points !== undefined;
}
/**
* converts a vec2 to a lat, lng uses the leaflet set in window.L
* @param {Number} x - x position
* @param {Number} y - y position
* @returns {Object} - the lat lng object
*/
export const convertVec2ToLatLng = (
x: number,
y: number
): { lat: number, lng: number } => {
const lat = window.L.unproject(
[x, -y + CONSTANTS.MAP_PROJECTION_SIZE],
CONSTANTS.MAP_PROJECTION_ZOOM
).lat;
const lng = window.L.unproject(
[x, -y + CONSTANTS.MAP_PROJECTION_SIZE],
CONSTANTS.MAP_PROJECTION_ZOOM
).lng;
return { lat, lng };
};
/** Given a marker create an id for it and return it as a string
* @param {object} marker - the marker to create an id for
* @returns {string} the marker id
*/
export function createMarkerId(marker: any): string {
const { lat, lng } = splitXYZToLatLng(marker.pos);
return `${lat}-${lng}-${marker.name}`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment