Geometric Groupby
The query functionality has been expanded to include four new geometric functions. These functions draw polygons on the map based on certain properties of the data retrieved by the query. Currently, only input data with a point (XY) column is supported, and the geometric functions are only applied when the query is materialized (for instance, when it is the source of a layer using subQuery):
  1. var q = ml.query().from("maplarge/Donors").where('State', 'EqualAny', 'AK,AL,AR,AZ').groupby('State').groupby("XY.concave_hull()");
  2. var map = ml.map("mapDiv", { lat: 39, lng: -98, z: 3});
  3. var layer = ml.layer(map, {
  4. query: {
  5. table: q.subQuery(),
  6. select: "geo.poly"
  7. },
Convex Hull

The convex hull of a set of points is the smallest convex polygon containing all of the points. This function uses the Andrew Monotone Chain algorithm which is described here. Our implementation supports multiple group by, so it is possible to generate the convex hulls of several sets of points at once.

Usage:  <point column>.convex_hull()

Example Query
  1. ml.query().from("maplarge/Donors").where('State', 'EqualAny', 'AK,...,WY').groupby('State').groupby("XY.convex_hull()");


Concave Hull

A concave hull (or alpha shape) of a set of points is a shape which contains all of the points in the set, but may be concave unlike the convex hull. There is not a single well defined concave hull for a given set of points, but the degree of concavity can be a controlled parameter. Starting with the Delaunay triangulation of the set, it is possible to successively remove edges longer than the parameter until a suitable shape is produced. In this case we are using the Chi algorithm described here. This implementation supports multiple group by, so it is possible to generate the convex hulls of several sets of points at once. Usage:

  1. Usage: <point column>.concave_hull([alpha shape parameter in METERS])

If the alpha shape parameter is omitted, the implementation will attempt to determine a “reasonable” value, which is the maximum length of the shortest edge of each triangle in the Delaunay triangulation.

Example Query
  1. ml.query().from("maplarge/Donors").where('State', 'EqualAny', 'AK,...,WY').groupby('State').groupby("XY.concave_hull(120000)");


Marching Squares

Marching squares is a well known algorithm designed to take a two dimensional field (grid) of real numbers and produce contour lines which represent specific levels. For example, it could be isobars on a weather map or lines of elevation on a topographical map. Since in most cases our data is not organized in a grid to begin with, the implementation needs to impose a field artificially. This is accomplished using morton codes and bit shifting. Once the data points are associated with grid squares, the algorithm proceeds generally as described here. Our implementation produces one group for each contour specified by the user, representing points which are greater than the threshold for that level and less than or equal to the threshold for the next level (if any). This can be useful for generating colored weather maps, etc. using rules based styling.

Usage: <point column>.marching_squares([grid zoom], <value column>, <threshold 1>, [thresh 2], ...)

The grid zoom parameter is optional. If not specified, the implementation will attempt to determine a “reasonable” value based on the shortest distance between points in the set, and the desire to avoid isolated grid cells.

The value column parameter should be a numeric column. String columns will work if non-numeric values are filtered out using a Where clause.

Example ml.query().from("test/test_precip").groupby("XY.marching_squares(5, Globvalue,0, 0.2, 0.4, 0.6, 0.8, 1.0)");


Marching Triangles

Marching (or “Meandering”) triangles is the application of the marching squares algorithm described above to a mesh of (probably irregular) triangles rather than to a uniform 2D field. This algorithm has the advantage that we don’t need to define a grid over the input points. Instead, we generate the Delaunay triangulation which is well defined. There is still a tuning parameter which specifies the maximum edge length for triangles which are to be considered in the algorithm. This parameter is important for generating visually appealing images without large triangular shaded areas. In other respects this implementation is functionally similar to Marching Squares. The performance is slightly worse due to the work involved in creating and adjusting the triangulation, but may produce better images for data sets which are not strongly grid-aligned.

Usage: <point column>.marching_triangles([max edge meters], <value column>, <threshold 1>, [threshold 2], ...)

If the “max edge meters” parameter is not specified, the implementation will attempt to determine a reasonable value by finding the average edge length and multiplying by 3. The value column parameter should be a numeric column. String columns will work if non-numeric values are filtered out using a Where clause.

Example ml.query().from("test/test_precip").groupby("XY.marching_triangles( Globvalue,0.0, 0.2, 0.4, 0.6, 0.8, 1.0)");


Voronoi Diagram

The Voronoi Diagram of a set of points produces a polygon for each point which represents the area of the plane closer to that point than any other. As such, the polygons cover the entire plane and the ones at the edges are infinite (not closed). The tessellation can be produced by computing the Delaunay triangulation and then generating the dual of that graph as described here. When combined with a reasonable clipping function to close the infinite polygons, Voronoi provides an elegant way to draw map regions shaded by point value. The result is similar to marching squares, but using a very different approach. The MapLarge implementation applies clipping either within a single bounding box, or within a square or circular region surrounding each point.

Usage:

  1. <point column>.voronoi([boundary in meters], [round|square])
  2. <point column>.voronoi([min degress longitude], [min degrees latitude], [max degrees longitude], [max degrees latitude])

or

  1. <point column>.voronoi.[boundary in meters].[round|square]
  2. <point column>.voronoi.[min degress longitude].[min degrees latitude].[max degrees longitude].[max degrees latitude]

The gray syntax is retained for backwards compatibility. If the clipping parameters are omitted, the infinite Voronoi polygons are clipped to the world bounds. The Voronoi group by produces one group for each input point.

Example ml.query().from("test/test_precip").groupby("XY.voronoi(3000, square)")