Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions android/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include <jni.h>
#include "react-native-clusterer.h"

extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativeclusterer_ClustererModule_initialize(JNIEnv *env, jclass clazz, jlong jsi) {
clusterer::install(*reinterpret_cast<facebook::jsi::Runtime *>(jsi));
extern "C" JNIEXPORT void JNICALL
Java_com_reactnativeclusterer_ClustererModule_initialize(JNIEnv *env,
jclass clazz,
jlong jsi) {
clusterer::install(*reinterpret_cast<facebook::jsi::Runtime *>(jsi));
clusterer::installHelpers(*reinterpret_cast<facebook::jsi::Runtime *>(jsi));
}
140 changes: 140 additions & 0 deletions cpp/helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,144 @@ void featurePropertyToJSI(
return;
}
}

double calculateDelta(double x, double y) {
if(x > y) {
return x - y;
}
return y - x;
}

double calculateAverage(initializer_list<double> args) {
if(args.size() == 0) {
return 0;
}

double sum = 0;
for(auto &num : args) sum += num;

return sum / args.size();
}

void installHelpers(jsi::Runtime &jsiRuntime) {
auto regionToBBox = jsi::Function::createFromHostFunction(
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "regionToBBox"), 1,
[](jsi::Runtime &runtime, const jsi::Value &thisValue,
const jsi::Value *arguments, size_t count) -> jsi::Array {
jsi::Object region = arguments[0].getObject(runtime);

double longitudeDelta =
region.getProperty(runtime, "longitudeDelta").asNumber();

double latitudeDelta =
region.getProperty(runtime, "latitudeDelta").asNumber();

double longitude = region.getProperty(runtime, "longitude").asNumber();

double latitude = region.getProperty(runtime, "latitude").asNumber();

double lngD = longitudeDelta;

if(longitudeDelta < 0) {
lngD = longitudeDelta + 360;
}

jsi::Array bbox = jsi::Array(runtime, 4);

bbox.setValueAtIndex(runtime, 0, longitude - lngD);
bbox.setValueAtIndex(runtime, 1, latitude - latitudeDelta);
bbox.setValueAtIndex(runtime, 2, longitude + lngD);
bbox.setValueAtIndex(runtime, 3, latitude + latitudeDelta);

return bbox;
});

auto getMarkersRegion = jsi::Function::createFromHostFunction(
jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "getMarkersRegion"), 1,
[](jsi::Runtime &runtime, const jsi::Value &thisValue,
const jsi::Value *arguments, size_t count) -> jsi::Object {
auto points = arguments[0].getObject(runtime).asArray(runtime);

jsi::Object initialValue =
points.getValueAtIndex(runtime, 0).asObject(runtime);

jsi::Object coordinates = jsi::Object(runtime);

coordinates.setProperty(
runtime, "minX",
initialValue.getProperty(runtime, "latitude").asNumber());

coordinates.setProperty(
runtime, "maxX",
initialValue.getProperty(runtime, "latitude").asNumber());

coordinates.setProperty(
runtime, "minY",
initialValue.getProperty(runtime, "longitude").asNumber());

coordinates.setProperty(
runtime, "maxY",
initialValue.getProperty(runtime, "longitude").asNumber());

for(int i = 0; i < points.size(runtime); i++) {
jsi::Object point =
points.getValueAtIndex(runtime, i).asObject(runtime);

double minX =
std::min(coordinates.getProperty(runtime, "minX").asNumber(),
point.getProperty(runtime, "latitude").asNumber());

double maxX =
std::max(coordinates.getProperty(runtime, "maxX").asNumber(),
point.getProperty(runtime, "latitude").asNumber());

double minY =
std::min(coordinates.getProperty(runtime, "minY").asNumber(),
point.getProperty(runtime, "longitude").asNumber());

double maxY =
std::max(coordinates.getProperty(runtime, "maxY").asNumber(),
point.getProperty(runtime, "longitude").asNumber());

coordinates.setProperty(runtime, "minX", minX);

coordinates.setProperty(runtime, "maxX", maxX);

coordinates.setProperty(runtime, "minY", minY);

coordinates.setProperty(runtime, "maxY", maxY);
}

double deltaX =
calculateDelta(coordinates.getProperty(runtime, "maxX").asNumber(),
coordinates.getProperty(runtime, "minX").asNumber());
double deltaY =
calculateDelta(coordinates.getProperty(runtime, "maxY").asNumber(),
coordinates.getProperty(runtime, "minY").asNumber());

jsi::Object region = jsi::Object(runtime);

region.setProperty(
runtime, "latitude",
calculateAverage(
{coordinates.getProperty(runtime, "minX").asNumber(),
coordinates.getProperty(runtime, "maxX").asNumber()}));

region.setProperty(
runtime, "longitude",
calculateAverage(
{coordinates.getProperty(runtime, "minY").asNumber(),
coordinates.getProperty(runtime, "maxY").asNumber()}));

region.setProperty(runtime, "latitudeDelta", deltaX * 1.5);
region.setProperty(runtime, "longitudeDelta", deltaY * 1.5);

return region;
});

jsiRuntime.global().setProperty(jsiRuntime, "regionToBBox",
std::move(regionToBBox));
jsiRuntime.global().setProperty(jsiRuntime, "getMarkersRegion",
std::move(getMarkersRegion));
}
} // namespace clusterer
10 changes: 6 additions & 4 deletions cpp/helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ void tileToJSI(jsi::Runtime &rt, jsi::Object &jsiObject,
mapbox::feature::feature<std::int16_t> &f,
jsi::Array &featuresInput);

void featurePropertyToJSI(jsi::Runtime &rt,
jsi::Object &jsiFeatureProperties,
std::pair<const std::string, mapbox::feature::value> &itr,
int &origFeatureIndex);
void featurePropertyToJSI(
jsi::Runtime &rt, jsi::Object &jsiFeatureProperties,
std::pair<const std::string, mapbox::feature::value> &itr,
int &origFeatureIndex);

void installHelpers(jsi::Runtime &jsiRuntime);

} // namespace clusterer
18 changes: 18 additions & 0 deletions ios/Clusterer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,22 @@ + (BOOL)requiresMainQueueSetup {
return @true;
}

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(installHelpers)
{
RCTBridge* bridge = [RCTBridge currentBridge];
RCTCxxBridge* cxxBridge = (RCTCxxBridge*)bridge;
if (cxxBridge == nil) {
return @false;
}

auto jsiRuntime = (jsi::Runtime*) cxxBridge.runtime;
if (jsiRuntime == nil) {
return @false;
}

clusterer::installHelpers(*(facebook::jsi::Runtime *)jsiRuntime);

return @true;
}

@end
17 changes: 15 additions & 2 deletions src/Supercluster.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { NativeModules, Platform } from 'react-native';
import GeoViewport from '@mapbox/geo-viewport';
import { getMarkersCoordinates, getMarkersRegion, regionToBBox } from './utils';
import { getMarkersCoordinates } from './utils';

import type * as GeoJSON from 'geojson';
import type { MapDimensions, Region } from './types';
import type { MapDimensions, Region, BBox, LatLng } from './types';
import type Supercluster from './types';

const module = NativeModules.Clusterer;
Expand All @@ -16,6 +16,19 @@ if (
module.install();
}

if (
module &&
typeof module.installHelpers === 'function' &&
!(global as any).regionToBBox &&
!(global as any).getMarkersRegion
) {
module.installHelpers();
}

const regionToBBox: (region: Region) => BBox = (global as any).regionToBBox;
const getMarkersRegion: (points: LatLng[]) => Region = (global as any)
.getMarkersRegion;

const createSupercluster = (global as any).createSupercluster;
const defaultOptions = {
minZoom: 0, // min zoom to generate clusters on
Expand Down
55 changes: 0 additions & 55 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,5 @@
import type { Feature, Point } from 'geojson';
import type Supercluster from './types';
import type { BBox, LatLng, Region } from './types';

const calculateDelta = (x: number, y: number): number =>
x > y ? x - y : y - x;

const calculateAverage = (...args: number[]): number => {
const argList = [...args];
if (!argList.length) {
return 0;
}
return argList.reduce((sum, num: number) => sum + num, 0) / argList.length;
};

export const regionToBBox = (region: Region): BBox => {
const lngD =
region.longitudeDelta < 0
? region.longitudeDelta + 360
: region.longitudeDelta;

return [
region.longitude - lngD, // westLng - min lng
region.latitude - region.latitudeDelta, // southLat - min lat
region.longitude + lngD, // eastLng - max lng
region.latitude + region.latitudeDelta, // northLat - max lat
];
};

export const getMarkersRegion = (points: LatLng[]): Region => {
const coordinates = {
minX: points[0]!.latitude,
maxX: points[0]!.latitude,
maxY: points[0]!.longitude,
minY: points[0]!.longitude,
};

const { maxX, minX, maxY, minY } = points.reduce(
(acc, point) => ({
minX: Math.min(acc.minX, point.latitude),
maxX: Math.max(acc.maxX, point.latitude),
minY: Math.min(acc.minY, point.longitude),
maxY: Math.max(acc.maxY, point.longitude),
}),
{ ...coordinates }
);

const deltaX = calculateDelta(maxX, minX);
const deltaY = calculateDelta(maxY, minY);

return {
latitude: calculateAverage(minX, maxX),
longitude: calculateAverage(minY, maxY),
latitudeDelta: deltaX * 1.5,
longitudeDelta: deltaY * 1.5,
};
};

export const getMarkersCoordinates = (markers: Feature<Point>) => {
const [longitude, latitude] = markers.geometry.coordinates;
Expand Down