Skip to content

Commit

Permalink
add Earth#getInitalBearingDegree method for initialBearing.add polyli…
Browse files Browse the repository at this point in the history
…ne#getCentroid and polyline#interpolate and polyline#project method
  • Loading branch information
王亮杰 committed May 19, 2020
1 parent 6beb5bb commit 1b1d846
Show file tree
Hide file tree
Showing 8 changed files with 379 additions and 2 deletions.
1 change: 1 addition & 0 deletions binding.gyp
Expand Up @@ -10,6 +10,7 @@
"./src/builder.cc",
"./src/cell.cc",
"./src/cell_id.cc",
"./src/earth.cc",
"./src/latlng.cc",
"./src/loop.cc",
"./src/point.cc",
Expand Down
185 changes: 185 additions & 0 deletions src/earth.cc
@@ -0,0 +1,185 @@
#include "earth.h"

Napi::FunctionReference Earth::constructor;

Napi::Object Earth::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);

Napi::Function func = DefineClass(env, "Earth", {
StaticMethod("toMeters", &Earth::ToMeters),
StaticMethod("toKm", &Earth::ToKm),
StaticMethod("getRadian", &Earth::GetRadian),
StaticMethod("getDegree", &Earth::GetDegree),
StaticMethod("getDistanceKm", &Earth::GetDistanceKm),
StaticMethod("getDistanceMeters", &Earth::GetDistanceMeters),
StaticMethod("getInitalBearingDegree", &Earth::GetInitalBearingDegree),
});

constructor = Napi::Persistent(func);
constructor.SuppressDestruct();

exports.Set("Earth", func);
return exports;
}

Earth::Earth(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Earth>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
}

bool Earth::PreCheck(const Napi::CallbackInfo &info, S2LatLng &latlng1, S2LatLng &latlng2){
int length = info.Length();

if(length < 2 || !info[0].IsObject() || !info[1].IsObject()){
return false;
}

Napi::Object objectA = info[0].As<Napi::Object>();
bool isLatlng1 = objectA.InstanceOf(LatLng::constructor.Value());
if (!isLatlng1) {
return false;
}

LatLng* ll1 = LatLng::Unwrap(objectA);
latlng1 = ll1->Get();

Napi::Object objectB = info[1].As<Napi::Object>();
bool isLatlng2 = objectB.InstanceOf(LatLng::constructor.Value());
if (!isLatlng2) {
return false;
}

LatLng* ll2 = LatLng::Unwrap(objectB);
latlng2 = ll2->Get();

return true;
}

Napi::Value Earth::ToMeters(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

S1Angle s1Angle = S1Angle(s2LatLngA, s2LatLngB);
double distance = S2Earth::ToMeters(s1Angle);

return Napi::Number::New(env, distance);
}

Napi::Value Earth::ToKm(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

S1Angle s1Angle = S1Angle(s2LatLngA, s2LatLngB);

double distance = S2Earth::ToKm(s1Angle);

return Napi::Number::New(env, distance);
}

Napi::Value Earth::GetRadian(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

S1Angle s1Angle = S1Angle(s2LatLngA, s2LatLngB);

double radians = s1Angle.radians();
return Napi::Number::New(env, radians);
}

Napi::Value Earth::GetDegree(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

S1Angle s1Angle = S1Angle(s2LatLngA, s2LatLngB);

double degrees = s1Angle.degrees();
return Napi::Number::New(env, degrees);
}

Napi::Value Earth::GetDistanceKm(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

double distance = S2Earth::GetDistanceKm(s2LatLngA, s2LatLngB);

return Napi::Number::New(env, distance);
}

Napi::Value Earth::GetDistanceMeters(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

double distance = S2Earth::GetDistanceMeters(s2LatLngA, s2LatLngB);

return Napi::Number::New(env, distance);
}

Napi::Value Earth::GetInitalBearingDegree(const Napi::CallbackInfo &info){
Napi::Env env = info.Env();

S2LatLng s2LatLngA;
S2LatLng s2LatLngB;

bool isValid = Earth::PreCheck(info, s2LatLngA, s2LatLngB);
if(!isValid){
Napi::TypeError::New(env, "2 Latlng Object expected.").ThrowAsJavaScriptException();
return env.Null();
}

S1Angle s1Angle = S2Earth::GetInitialBearing(s2LatLngA, s2LatLngB);

double degrees = s1Angle.degrees();
return Napi::Number::New(env, degrees);
}

S2Earth Earth::Get() {
return this->s2earth;
}
32 changes: 32 additions & 0 deletions src/earth.h
@@ -0,0 +1,32 @@
#ifndef RADAR_EARTH
#define RADAR_EARTH

#include <napi.h>
#include "latlng.h"
#include "s2/s1angle.h"
#include "s2/s2latlng.h"
#include "s2/s2earth.h"

class Earth : public Napi::ObjectWrap<Earth> {
public:
Earth(const Napi::CallbackInfo& info);
S2Earth Get();

static Napi::FunctionReference constructor;
static Napi::Object Init(Napi::Env env, Napi::Object exports);

private:
static Napi::Value ToMeters(const Napi::CallbackInfo &info);
static Napi::Value ToKm(const Napi::CallbackInfo &info);
static Napi::Value GetRadian(const Napi::CallbackInfo &info);
static Napi::Value GetDegree(const Napi::CallbackInfo &info);
static Napi::Value GetDistanceKm(const Napi::CallbackInfo &info);
static Napi::Value GetDistanceMeters(const Napi::CallbackInfo &info);
static Napi::Value GetInitalBearingDegree(const Napi::CallbackInfo &info);

static bool PreCheck(const Napi::CallbackInfo &info, S2LatLng &latlng1, S2LatLng &latlng2);

S2Earth s2earth;
};

#endif
51 changes: 51 additions & 0 deletions src/polyline.cc
Expand Up @@ -9,6 +9,9 @@ Napi::Object Polyline::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("contains", &Polyline::Contains),
InstanceMethod("nearlyCovers", &Polyline::NearlyCovers),
InstanceMethod("getLength", &Polyline::GetLength),
InstanceMethod("getCentroid", &Polyline::GetCentroid),
InstanceMethod("interpolate", &Polyline::Interpolate),
InstanceMethod("project",&Polyline::Project),
});

constructor = Napi::Persistent(func);
Expand Down Expand Up @@ -120,3 +123,51 @@ Napi::Value Polyline::GetLength(const Napi::CallbackInfo& info){

return Napi::Number::New(env, length);
}

Napi::Value Polyline::GetCentroid(const Napi::CallbackInfo& info){

S2Point s2point = this->s2polyline.GetCentroid();
S2LatLng s2latlng = S2LatLng(s2point);

return Napi::String::New(info.Env(), s2latlng.ToStringInDegrees());
}

Napi::Value Polyline::Interpolate(const Napi::CallbackInfo& info){
Napi::Env env = info.Env();
int length = info.Length();

if(length != 1 || !info[0].IsNumber()){
Napi::TypeError::New(env, "expected fraction").ThrowAsJavaScriptException();
return env.Null();
}
double fraction = info[0].As<Napi::Number>().DoubleValue();
S2Point s2point = this->s2polyline.Interpolate(fraction);
S2LatLng s2latlng = S2LatLng(s2point);
return Napi::String::New(info.Env(), s2latlng.ToStringInDegrees());
}

Napi::Value Polyline::Project(const Napi::CallbackInfo& info){
Napi::Env env = info.Env();
int length = info.Length();

if(length != 1 || !info[0].IsObject()){
Napi::TypeError::New(env, "expected LatLng object").ThrowAsJavaScriptException();
return env.Null();
}

Napi::Object object = info[0].As<Napi::Object>();
bool isLatlng = object.InstanceOf(LatLng::constructor.Value());
if (!isLatlng) {
Napi::TypeError::New(env, "expected LatLng object").ThrowAsJavaScriptException();
return env.Null();
}

LatLng* ll = LatLng::Unwrap(object);
S2LatLng sourceLatLng = ll->Get();

int index;

S2Point s2point = this->s2polyline.Project(sourceLatLng.ToPoint(), &index);
S2LatLng s2latlng = S2LatLng(s2point);
return Napi::String::New(info.Env(), s2latlng.ToStringInDegrees());
}
3 changes: 3 additions & 0 deletions src/polyline.h
Expand Up @@ -23,6 +23,9 @@ class Polyline : public Napi::ObjectWrap<Polyline> {
Napi::Value Contains(const Napi::CallbackInfo &info);
Napi::Value NearlyCovers(const Napi::CallbackInfo &info);
Napi::Value GetLength(const Napi::CallbackInfo &info);
Napi::Value GetCentroid(const Napi::CallbackInfo &info);
Napi::Value Interpolate(const Napi::CallbackInfo &info);
Napi::Value Project(const Napi::CallbackInfo &info);

S2Polyline s2polyline;
};
Expand Down
2 changes: 2 additions & 0 deletions src/s2.cc
Expand Up @@ -2,6 +2,7 @@
#include "cell.h"
#include "cell_id.h"
#include "cell_union.h"
#include "earth.h"
#include "latlng.h"
#include "loop.h"
#include "point.h"
Expand All @@ -13,6 +14,7 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
Builder::Init(env, exports);
Cell::Init(env, exports);
CellId::Init(env, exports);
Earth::Init(env, exports);
LatLng::Init(env, exports);
Loop::Init(env, exports);
Point::Init(env, exports);
Expand Down
51 changes: 49 additions & 2 deletions test/Polyline.test.js
Expand Up @@ -14,7 +14,7 @@ const baiCauseway_point = [30.257775, 120.144291];
test("Polyline#constructor accepts LatLng array", () => {

const lls = baiCauseway.map((latlng) => {
const [lng, lat] = latlng;
const [lat, lng] = latlng;
return new s2.LatLng(lat, lng);
});
const polyline = new s2.Polyline(lls);
Expand Down Expand Up @@ -72,7 +72,7 @@ test("Polyline#NearlyCovers false", () => {
});
const newPolyline = new s2.Polyline(newLls);

expect(polyline.nearlyCovers(newPolyline, 1e-3)).toBe(false);
expect(polyline.nearlyCovers(newPolyline, 1e-5)).toBe(false);
});

test("Polyline#getLength", () => {
Expand All @@ -84,3 +84,50 @@ test("Polyline#getLength", () => {

expect(polyline.getLength()).toBe(805.4416481053961);
});

test("Polyline#getCentroid", () => {
const lls = baiCauseway.map((latlng) => {
const [lat, lng] = latlng;
return new s2.LatLng(lat, lng);
});
const polyline = new s2.Polyline(lls);
const latlngStr = polyline.getCentroid();
//console.log("getCentroid: ", latlngStr);

expect(polyline.getCentroid()).toBe("30.258369,120.144809");
});

test("Polyline#interpolate", () => {
const lls = baiCauseway.map((latlng) => {
const [lat, lng] = latlng;
return new s2.LatLng(lat, lng);
});

const polyline = new s2.Polyline(lls);
const latlngStr = polyline.interpolate(3/4);
//console.log('interpolate: latlngStr: ', latlngStr);

expect(polyline.interpolate(3/4)).toBe("30.256914,120.143561");
});

test("poly#project", () => {
// baiCauseway polyline
const lls = baiCauseway.map((latlng) => {
const [lat, lng] = latlng;
return new s2.LatLng(lat, lng);
});
const polyline = new s2.Polyline(lls);

const westLakeMuseum = [30.259710, 120.140802];//西湖博览会博物馆
const westLakeMuseumLatLng = new s2.LatLng(westLakeMuseum[0], westLakeMuseum[1]);
expect(polyline.project(westLakeMuseumLatLng)).toBe("30.257633,120.144162");

const midLakePavilion = [30.248636, 120.139723];// 西湖湖心亭
const midLakePavilionLatLng = new s2.LatLng(midLakePavilion[0], midLakePavilion[1]);

expect(polyline.project(midLakePavilionLatLng)).toBe("30.255445,120.142336");
// console.log('midLakePavilionLatLng: ', polyline.project(midLakePavilionLatLng));
// console.log('westLakeMuseumLatLng: ', polyline.project(westLakeMuseumLatLng));
})


0 comments on commit 1b1d846

Please sign in to comment.