Created
May 22, 2019 15:52
-
-
Save csprance/da865f45c0ea9c4b768d76079d38aa8b to your computer and use it in GitHub Desktop.
More Map Data Functions in FlowType Javascript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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