first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@ -0,0 +1,3 @@
from .viewport_helpers import compute_view # noqa
from .type_checking import has_geo_interface, is_pandas_df, records_from_geo_interface # noqa
from .color_scales import assign_random_colors # noqa

View File

@ -0,0 +1,61 @@
from collections import defaultdict
import numpy as np
# Grafted from
# https://github.com/maartenbreddels/ipyvolume/blob/d13828dfd8b57739004d5daf7a1d93ad0839ed0f/ipyvolume/serialize.py#L219
def array_to_binary(ar, obj=None, force_contiguous=True):
if ar is None:
return None
if ar.dtype.kind not in ["u", "i", "f"]: # ints and floats
raise ValueError("unsupported dtype: %s" % (ar.dtype))
# WebGL does not support float64, case it here
if ar.dtype == np.float64:
ar = ar.astype(np.float32)
# JS does not support int64
if ar.dtype == np.int64:
ar = ar.astype(np.int32)
# make sure it's contiguous
if force_contiguous and not ar.flags["C_CONTIGUOUS"]:
ar = np.ascontiguousarray(ar)
return {
# binary data representation of a numpy matrix
"value": memoryview(ar),
# dtype convertible to a typed array
"dtype": str(ar.dtype),
# height of np matrix
"length": ar.shape[0],
# width of np matrix
"size": 1 if len(ar.shape) == 1 else ar.shape[1],
}
def serialize_columns(data_set_cols, obj=None):
if data_set_cols is None:
return None
layers = defaultdict(dict)
# Number of records in data set
length = {}
for col in data_set_cols:
accessor_attribute = array_to_binary(col["np_data"])
if length.get(col["layer_id"]):
length[col["layer_id"]] = max(length[col["layer_id"]], accessor_attribute["length"])
else:
length[col["layer_id"]] = accessor_attribute["length"]
# attributes is deck.gl's expected argument name for
# binary data transfer
if not layers[col["layer_id"]].get("attributes"):
layers[col["layer_id"]]["attributes"] = {}
# Add new accessor
layers[col["layer_id"]]["attributes"][col["accessor"]] = {
"value": accessor_attribute["value"],
"dtype": accessor_attribute["dtype"],
"size": accessor_attribute["size"],
}
for layer_key, _ in layers.items():
layers[layer_key]["length"] = length[layer_key]
return layers
data_buffer_serialization = dict(to_json=serialize_columns, from_json=None)

View File

@ -0,0 +1,34 @@
from collections import OrderedDict
import random
def get_random_rgb():
"""Generate a random RGB value
Returns
-------
list of float
Random RGB array
"""
return [round(random.random() * 255) for _ in range(0, 3)]
def assign_random_colors(data_vector):
"""Produces lookup table keyed by each class of data, with value as an RGB array
Parameters
---------
data_vector : list
Vector of data classes to be categorized, passed from the data itself
Returns
-------
collections.OrderedDict
Dictionary of random RGBA value per class, keyed on class
"""
deduped_classes = list(set(data_vector))
classes = sorted([str(x) for x in deduped_classes])
colors = []
for _ in classes:
colors.append(get_random_rgb())
return OrderedDict([item for item in zip(classes, colors)])

View File

@ -0,0 +1,24 @@
def is_pandas_df(obj):
"""Check if an object is a Pandas DataFrame
Returns
-------
bool
Returns True if object is a Pandas DataFrame and False otherwise
"""
return obj.__class__.__module__ == "pandas.core.frame" and obj.to_records and obj.to_dict
def has_geo_interface(obj):
return hasattr(obj, "__geo_interface__")
def records_from_geo_interface(data):
"""Un-nest data from object implementing __geo_interface__ standard"""
flattened_records = []
for d in data.__geo_interface__.get("features"):
record = d.get("properties", {})
geom = d.get("geometry", {})
record["geometry"] = geom
flattened_records.append(record)
return flattened_records

View File

@ -0,0 +1,176 @@
"""
Functions that make it easier to provide a default centering
for a view state
"""
import math
from ..bindings.view_state import ViewState
from .type_checking import is_pandas_df
def _squared_diff(x, x0):
return (x0 - x) * (x0 - x)
def euclidean(y, y1):
"""Euclidean distance in n-dimensions
Parameters
----------
y : tuple of float
A point in n-dimensions
y1 : tuple of float
A point in n-dimensions
Examples
--------
>>> EPSILON = 0.001
>>> euclidean((3, 6, 5), (7, -5, 1)) - 12.369 < EPSILON
True
"""
if not len(y) == len(y1):
raise Exception("Input coordinates must be of the same length")
return math.sqrt(sum([_squared_diff(x, x0) for x, x0 in zip(y, y1)]))
def geometric_mean(points):
"""Gets centroid in a series of points
Parameters
----------
points : list of list of float
List of (x, y) coordinates
Returns
-------
tuple
The centroid of a list of points
"""
avg_x = sum([float(p[0]) for p in points]) / len(points)
avg_y = sum([float(p[1]) for p in points]) / len(points)
return (avg_x, avg_y)
def get_bbox(points):
"""Get the bounding box around the data,
Parameters
----------
points : list of list of float
List of (x, y) coordinates
Returns
-------
dict
Dictionary containing the top left and bottom right points of a bounding box
"""
xs = [p[0] for p in points]
ys = [p[1] for p in points]
max_x = max(xs)
max_y = max(ys)
min_x = min(xs)
min_y = min(ys)
return ((min_x, max_y), (max_x, min_y))
def k_nearest_neighbors(points, center, k):
"""Gets the k furthest points from the center
Parameters
----------
points : list of list of float
List of (x, y) coordinates
center : list of list of float
Center point
k : int
Number of points
Returns
-------
list
Index of the k furthest points
Todo
---
Currently implemently naively, needs to be more efficient
"""
pts_with_distance = [(pt, euclidean(pt, center)) for pt in points]
sorted_pts = sorted(pts_with_distance, key=lambda x: x[1])
return [x[0] for x in sorted_pts][: int(k)]
def get_n_pct(points, proportion=1):
"""Computes the bounding box of the maximum zoom for the specified list of points
Parameters
----------
points : list of list of float
List of (x, y) coordinates
proportion : float, default 1
Value between 0 and 1 representing the minimum proportion of data to be captured
Returns
-------
list
k nearest data points
"""
if proportion == 1:
return points
# Compute the medioid of the data
centroid = geometric_mean(points)
# Retain the closest n*proportion points
n_to_keep = math.floor(proportion * len(points))
return k_nearest_neighbors(points, centroid, n_to_keep)
def bbox_to_zoom_level(bbox):
"""Computes the zoom level of a lat/lng bounding box
Parameters
----------
bbox : list of list of float
Northwest and southeast corners of a bounding box, given as two points in a list
Returns
-------
int
Zoom level of map in a WGS84 Mercator projection (e.g., like that of Google Maps)
"""
lat_diff = max(bbox[0][0], bbox[1][0]) - min(bbox[0][0], bbox[1][0])
lng_diff = max(bbox[0][1], bbox[1][1]) - min(bbox[0][1], bbox[1][1])
max_diff = max(lng_diff, lat_diff)
zoom_level = None
if max_diff < (360.0 / math.pow(2, 20)):
zoom_level = 21
else:
zoom_level = int(-1 * ((math.log(max_diff) / math.log(2.0)) - (math.log(360.0) / math.log(2))))
if zoom_level < 1:
zoom_level = 1
return zoom_level
def compute_view(points, view_proportion=1, view_type=ViewState):
"""Automatically computes a zoom level for the points passed in.
Parameters
----------
points : list of list of float or pandas.DataFrame
A list of points
view_propotion : float, default 1
Proportion of the data that is meaningful to plot
view_type : class constructor for pydeck.ViewState, default :class:`pydeck.bindings.view_state.ViewState`
Class constructor for a viewport. In the current version of pydeck,
users most likely do not have to modify this attribute.
Returns
-------
pydeck.Viewport
Viewport fitted to the data
"""
if is_pandas_df(points):
points = points.to_records(index=False)
bbox = get_bbox(get_n_pct(points, view_proportion))
zoom = bbox_to_zoom_level(bbox)
center = geometric_mean(points)
instance = view_type(latitude=center[1], longitude=center[0], zoom=zoom)
return instance