Skip to content

Commit

Permalink
Merge pull request #2 from lucka-me/fix-improve-performance
Browse files Browse the repository at this point in the history
Fix issues and improve performance
  • Loading branch information
lucka-me authored Apr 22, 2023
2 parents e8bd025 + 518e27c commit 2c73a58
Show file tree
Hide file tree
Showing 16 changed files with 552 additions and 471 deletions.
9 changes: 2 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
Package.resolved

/.vscode
/.vscode
/.swiftpm
2 changes: 2 additions & 0 deletions .tokeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Package.swift
*.md
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let package = Package(
.executable(name: "ingress-drone-explorer", targets: [ "ingress-drone-explorer" ])
],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "1.2.2")),
],
targets: [
.executableTarget(
Expand Down
73 changes: 56 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
# Ingress Drone Explorer - Swift

[![CI](https://github.com/lucka-me/ingress-drone-explorer-swift/actions/workflows/ci.yml/badge.svg)](https://github.com/lucka-me/ingress-drone-explorer-swift/actions/workflows/ci.yml "CI Workflow")
[![Lines of code][swift-loc]][swift-repo]

An offline CLI tool to analyze reachable Portals for Ingress Drone Mark I.

A C++ implementation is also [available](https://github.com/lucka-me/ingress-drone-explorer-cpp).
The Swift implementation is slower but the code is much simpler.
Implementations in different languages are listed and compared in [Benchmark](#benchmark).

The CI workflow builds universal binary for macOS and x86_64 binary for Linux, the file is available as artifact.

The code itself is ready for Windows, but the compiler doesn't link Swift runtime statically. So the exe is not able to run without Swift dlls, and the workflow to build for Windows is disabled.

## Build
## Build from Source

Install the latest Swift, clone the repository and simply:
### Requirements

- Swift 5.8 (Backporting should be possible and easy)

### Build

```sh
$ swift package resolve
$ swift build
```

## Usage
## Exploration Guide

### Prepare Files

Expand All @@ -39,25 +43,60 @@ All the files should be JSON.
```
2. Portal Key list file, should be an array of GUID (Not required but strongly recommended)

Maybe an IITC plugin like [this](https://github.com/lucka-me/toolkit/tree/master/Ingress/Portal-List-Exporter) can help.
Maybe an IITC plugin like [this](https://github.com/lucka-me/toolkit/tree/master/Ingress/Portal-List-Exporter) helps.

### Run
### Usage

```sh
$ `swift build --show-bin-path`/ingress-drone-explorer <path-to-portal-list-files> -s <start point>
```
$ ingress-drone-explorer <portal-list-files> -s <longitude,latitude> [options...]
```

#### Options

Explore with key list:
```sh
$ `swift build --show-bin-path`/ingress-drone-explorer ... -k <path-to-key-list-file>
```
$ ... -k <key-list-file>
```

Output cells for IITC Draw tools:
```sh
$ `swift build --show-bin-path`/ingress-drone-explorer ... --output-drawn-items <path-to-output>
Output cells JSON for IITC Draw tools:
```
$ ... --output-drawn-items <output-file>
```

Help information:
```sh
$ `swift build --show-bin-path`/ingress-drone-explorer -h
```
```
$ ingress-drone-explorer -h
```

## Benchmark

### Sample Data

- Area: Shenzhen downtown and Hong Kong
- Portals: 34,041 Portals in 13,451 cells
- Keys: 11 matched
- Start Point: Shenzhen Bay Sports Center
- Result: 30,462 Portals and 11,342 cells are reachable

### Result

Average exploration time consumed of 100 executions on MacBook Air (M2).

| Implementation | Lines of Code | Commit | Consumed
| ---------------------: | :-------------: | :----------------------------------: | :---
| [Swift][swift-repo] | ![][swift-loc] | `Current` | 0.722 s
| [C++][cpp-repo] | ![][cpp-loc] | [`db5a976`][cpp-benchmark-commit] | 0.583 s
| [Node.js][nodejs-repo] | ![][nodejs-loc] | [`7ad90e9`][nodejs-benchmark-commit] | 1.295 s

The results of other implementations may be outdated, please check their repositories for latest results.

[swift-repo]: https://github.com/lucka-me/ingress-drone-explorer-swift
[swift-loc]: https://img.shields.io/tokei/lines/github/lucka-me/ingress-drone-explorer-swift

[cpp-repo]: https://github.com/lucka-me/ingress-drone-explorer-cpp
[cpp-loc]: https://img.shields.io/tokei/lines/github/lucka-me/ingress-drone-explorer-cpp
[cpp-benchmark-commit]: https://github.com/lucka-me/ingress-drone-explorer-cpp/commit/db5a976

[nodejs-repo]: https://github.com/lucka-me/ingress-drone-explorer-nodejs
[nodejs-loc]: https://img.shields.io/tokei/lines/github/lucka-me/ingress-drone-explorer-nodejs
[nodejs-benchmark-commit]: https://github.com/lucka-me/ingress-drone-explorer-nodejs/commit/7ad90e9
91 changes: 91 additions & 0 deletions Sources/ingress-drone-explorer/Definitions/Coordinate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import Foundation

struct Coordinate {
var lng: Double = 0
var lat: Double = 0
}

extension Coordinate {
init?(_ text: String) {
let components = text.split(separator: ",")
if components.count != 2 { return nil }
guard
let lng = Double(components[0]),
abs(lng) <= 180
else {
return nil
}
guard
let lat = Double(components[1]),
abs(lat) <= 90
else {
return nil
}
self.lng = lng
self.lat = lat
}
}

extension Coordinate: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
lng = try container.decode(Double.self, forKey: .lng)
guard abs(lng) <= 180 else {
throw DecodingError.dataCorruptedError(
forKey: .lng, in: container, debugDescription: "The lng is not in range of [-180, 180]."
)
}
lat = try container.decode(Double.self, forKey: .lat)
guard abs(lat) <= 180 else {
throw DecodingError.dataCorruptedError(
forKey: .lat, in: container, debugDescription: "The lat is not in range of [-90, 90]."
)
}
}
}

extension Coordinate {

private static let earthRadius = 6371008.8

var theta: Double {
lng * Double.pi / 180.0
}

var phi: Double {
lat * Double.pi / 180.0
}

func distance(to other: Coordinate) -> Double {
let sinT = sin((other.theta - theta) / 2)
let sinP = sin((other.phi - phi) / 2)
let a = sinP * sinP + sinT * sinT * cos(phi) * cos(other.phi)
return atan2(sqrt(a), sqrt(1 - a)) * 2 * Self.earthRadius
}

func distance(to segment: (a: Coordinate, b: Coordinate)) -> Double {
let c1 = (segment.b.lat - segment.a.lat) * (lat - segment.a.lat)
+ (segment.b.lng - segment.a.lng) * (lng - segment.a.lng)
if c1 <= 0 {
return distance(to: segment.a)
}
let c2 = (segment.b.lat - segment.a.lat) * (segment.b.lat - segment.a.lat)
+ (segment.b.lng - segment.a.lng) * (segment.b.lng - segment.a.lng)
if c2 <= c1 {
return distance(to: segment.b)
}
let ratio = c1 / c2;
return distance(to:
.init(
lng: segment.a.lng + ratio * (segment.b.lng - segment.a.lng),
lat: segment.a.lat + ratio * (segment.b.lat - segment.a.lat)
)
)
}

func closer(to a: Coordinate, than b: Coordinate) -> Bool {
let dA = (lng - a.lng) * (lng - a.lng) + (lat - a.lat) * (lat - a.lat)
let dB = (lng - b.lng) * (lng - b.lng) + (lat - b.lat) * (lat - b.lat)
return dA < dB
}
}
22 changes: 22 additions & 0 deletions Sources/ingress-drone-explorer/Definitions/Portal.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
struct Portal : Decodable {
var guid = ""
var title: String? = nil
var coordinate = Coordinate()
}

extension Portal : Hashable {
static func ==(lhs: Portal, rhs: Portal) -> Bool {
lhs.guid == rhs.guid
}

func hash(into hasher: inout Hasher) {
hasher.combine(guid)
}
}

extension Portal {
enum CodingKeys: String, CodingKey {
case guid, title
case coordinate = "lngLat"
}
}
Loading

0 comments on commit 2c73a58

Please sign in to comment.