mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-01-24 07:56:52 +00:00
Initial implementation of Google's Open Location Code conversion
This commit is contained in:
parent
d3aca065ab
commit
9c5f4a67c2
202
plugins/tiddlywiki/geospatial/files/open-location-code/LICENSE
Normal file
202
plugins/tiddlywiki/geospatial/files/open-location-code/LICENSE
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -0,0 +1,695 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* Convert locations to and from short codes.
|
||||
*
|
||||
* Open Location Codes are short, 10-11 character codes that can be used instead
|
||||
* of street addresses. The codes can be generated and decoded offline, and use
|
||||
* a reduced character set that minimises the chance of codes including words.
|
||||
*
|
||||
* Codes are able to be shortened relative to a nearby location. This means that
|
||||
* in many cases, only four to seven characters of the code are needed.
|
||||
* To recover the original code, the same location is not required, as long as
|
||||
* a nearby location is provided.
|
||||
*
|
||||
* Codes represent rectangular areas rather than points, and the longer the
|
||||
* code, the smaller the area. A 10 character code represents a 13.5x13.5
|
||||
* meter area (at the equator. An 11 character code represents approximately
|
||||
* a 2.8x3.5 meter area.
|
||||
*
|
||||
* Two encoding algorithms are used. The first 10 characters are pairs of
|
||||
* characters, one for latitude and one for longitude, using base 20. Each pair
|
||||
* reduces the area of the code by a factor of 400. Only even code lengths are
|
||||
* sensible, since an odd-numbered length would have sides in a ratio of 20:1.
|
||||
*
|
||||
* At position 11, the algorithm changes so that each character selects one
|
||||
* position from a 4x5 grid. This allows single-character refinements.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Encode a location, default accuracy:
|
||||
* var code = OpenLocationCode.encode(47.365590, 8.524997);
|
||||
*
|
||||
* Encode a location using one stage of additional refinement:
|
||||
* var code = OpenLocationCode.encode(47.365590, 8.524997, 11);
|
||||
*
|
||||
* Decode a full code:
|
||||
* var coord = OpenLocationCode.decode(code);
|
||||
* var msg = 'Center is ' + coord.latitudeCenter + ',' + coord.longitudeCenter;
|
||||
*
|
||||
* Attempt to trim the first characters from a code:
|
||||
* var shortCode = OpenLocationCode.shorten('8FVC9G8F+6X', 47.5, 8.5);
|
||||
*
|
||||
* Recover the full code from a short code:
|
||||
* var code = OpenLocationCode.recoverNearest('9G8F+6X', 47.4, 8.6);
|
||||
* var code = OpenLocationCode.recoverNearest('8F+6X', 47.4, 8.6);
|
||||
*/
|
||||
(function(root, factory) {
|
||||
/* global define, module */
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['b'], function(b) {
|
||||
return (root.returnExportsGlobal = factory(b));
|
||||
});
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like environments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory(require('b'));
|
||||
} else {
|
||||
// Browser globals
|
||||
root.OpenLocationCode = factory();
|
||||
}
|
||||
}(this, function() {
|
||||
var OpenLocationCode = {};
|
||||
|
||||
/**
|
||||
* Provides a normal precision code, approximately 14x14 meters.
|
||||
* @const {number}
|
||||
*/
|
||||
OpenLocationCode.CODE_PRECISION_NORMAL = 10;
|
||||
|
||||
/**
|
||||
* Provides an extra precision code, approximately 2x3 meters.
|
||||
* @const {number}
|
||||
*/
|
||||
OpenLocationCode.CODE_PRECISION_EXTRA = 11;
|
||||
|
||||
// A separator used to break the code into two parts to aid memorability.
|
||||
var SEPARATOR_ = '+';
|
||||
|
||||
// The number of characters to place before the separator.
|
||||
var SEPARATOR_POSITION_ = 8;
|
||||
|
||||
// The character used to pad codes.
|
||||
var PADDING_CHARACTER_ = '0';
|
||||
|
||||
// The character set used to encode the values.
|
||||
var CODE_ALPHABET_ = '23456789CFGHJMPQRVWX';
|
||||
|
||||
// The base to use to convert numbers to/from.
|
||||
var ENCODING_BASE_ = CODE_ALPHABET_.length;
|
||||
|
||||
// The maximum value for latitude in degrees.
|
||||
var LATITUDE_MAX_ = 90;
|
||||
|
||||
// The maximum value for longitude in degrees.
|
||||
var LONGITUDE_MAX_ = 180;
|
||||
|
||||
// The max number of digits to process in a plus code.
|
||||
var MAX_DIGIT_COUNT_ = 15;
|
||||
|
||||
// Maximum code length using lat/lng pair encoding. The area of such a
|
||||
// code is approximately 13x13 meters (at the equator), and should be suitable
|
||||
// for identifying buildings. This excludes prefix and separator characters.
|
||||
var PAIR_CODE_LENGTH_ = 10;
|
||||
|
||||
// First place value of the pairs (if the last pair value is 1).
|
||||
var PAIR_FIRST_PLACE_VALUE_ = Math.pow(
|
||||
ENCODING_BASE_, (PAIR_CODE_LENGTH_ / 2 - 1));
|
||||
|
||||
// Inverse of the precision of the pair section of the code.
|
||||
var PAIR_PRECISION_ = Math.pow(ENCODING_BASE_, 3);
|
||||
|
||||
// The resolution values in degrees for each position in the lat/lng pair
|
||||
// encoding. These give the place value of each position, and therefore the
|
||||
// dimensions of the resulting area.
|
||||
var PAIR_RESOLUTIONS_ = [20.0, 1.0, .05, .0025, .000125];
|
||||
|
||||
// Number of digits in the grid precision part of the code.
|
||||
var GRID_CODE_LENGTH_ = MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_;
|
||||
|
||||
// Number of columns in the grid refinement method.
|
||||
var GRID_COLUMNS_ = 4;
|
||||
|
||||
// Number of rows in the grid refinement method.
|
||||
var GRID_ROWS_ = 5;
|
||||
|
||||
// First place value of the latitude grid (if the last place is 1).
|
||||
var GRID_LAT_FIRST_PLACE_VALUE_ = Math.pow(
|
||||
GRID_ROWS_, (GRID_CODE_LENGTH_ - 1));
|
||||
|
||||
// First place value of the longitude grid (if the last place is 1).
|
||||
var GRID_LNG_FIRST_PLACE_VALUE_ = Math.pow(
|
||||
GRID_COLUMNS_, (GRID_CODE_LENGTH_ - 1));
|
||||
|
||||
// Multiply latitude by this much to make it a multiple of the finest
|
||||
// precision.
|
||||
var FINAL_LAT_PRECISION_ = PAIR_PRECISION_ *
|
||||
Math.pow(GRID_ROWS_, (MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_));
|
||||
|
||||
// Multiply longitude by this much to make it a multiple of the finest
|
||||
// precision.
|
||||
var FINAL_LNG_PRECISION_ = PAIR_PRECISION_ *
|
||||
Math.pow(GRID_COLUMNS_, (MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_));
|
||||
|
||||
// Minimum length of a code that can be shortened.
|
||||
var MIN_TRIMMABLE_CODE_LEN_ = 6;
|
||||
|
||||
/**
|
||||
@return {string} Returns the OLC alphabet.
|
||||
*/
|
||||
OpenLocationCode.getAlphabet = function() {
|
||||
return CODE_ALPHABET_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if a code is valid.
|
||||
*
|
||||
* To be valid, all characters must be from the Open Location Code character
|
||||
* set with at most one separator. The separator can be in any even-numbered
|
||||
* position up to the eighth digit.
|
||||
*
|
||||
* @param {string} code The string to check.
|
||||
* @return {boolean} True if the string is a valid code.
|
||||
*/
|
||||
var isValid = OpenLocationCode.isValid = function(code) {
|
||||
if (!code || typeof code !== 'string') {
|
||||
return false;
|
||||
}
|
||||
// The separator is required.
|
||||
if (code.indexOf(SEPARATOR_) == -1) {
|
||||
return false;
|
||||
}
|
||||
if (code.indexOf(SEPARATOR_) != code.lastIndexOf(SEPARATOR_)) {
|
||||
return false;
|
||||
}
|
||||
// Is it the only character?
|
||||
if (code.length == 1) {
|
||||
return false;
|
||||
}
|
||||
// Is it in an illegal position?
|
||||
if (code.indexOf(SEPARATOR_) > SEPARATOR_POSITION_ ||
|
||||
code.indexOf(SEPARATOR_) % 2 == 1) {
|
||||
return false;
|
||||
}
|
||||
// We can have an even number of padding characters before the separator,
|
||||
// but then it must be the final character.
|
||||
if (code.indexOf(PADDING_CHARACTER_) > -1) {
|
||||
// Short codes cannot have padding
|
||||
if (code.indexOf(SEPARATOR_) < SEPARATOR_POSITION_) {
|
||||
return false;
|
||||
}
|
||||
// Not allowed to start with them!
|
||||
if (code.indexOf(PADDING_CHARACTER_) == 0) {
|
||||
return false;
|
||||
}
|
||||
// There can only be one group and it must have even length.
|
||||
var padMatch = code.match(new RegExp('(' + PADDING_CHARACTER_ + '+)', 'g'));
|
||||
if (padMatch.length > 1 || padMatch[0].length % 2 == 1 ||
|
||||
padMatch[0].length > SEPARATOR_POSITION_ - 2) {
|
||||
return false;
|
||||
}
|
||||
// If the code is long enough to end with a separator, make sure it does.
|
||||
if (code.charAt(code.length - 1) != SEPARATOR_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// If there are characters after the separator, make sure there isn't just
|
||||
// one of them (not legal).
|
||||
if (code.length - code.indexOf(SEPARATOR_) - 1 == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Strip the separator and any padding characters.
|
||||
code = code.replace(new RegExp('\\' + SEPARATOR_ + '+'), '')
|
||||
.replace(new RegExp(PADDING_CHARACTER_ + '+'), '');
|
||||
// Check the code contains only valid characters.
|
||||
for (var i = 0, len = code.length; i < len; i++) {
|
||||
var character = code.charAt(i).toUpperCase();
|
||||
if (character != SEPARATOR_ && CODE_ALPHABET_.indexOf(character) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if a code is a valid short code.
|
||||
*
|
||||
* @param {string} code The string to check.
|
||||
* @return {boolean} True if the string can be produced by removing four or
|
||||
* more characters from the start of a valid code.
|
||||
*/
|
||||
var isShort = OpenLocationCode.isShort = function(code) {
|
||||
// Check it's valid.
|
||||
if (!isValid(code)) {
|
||||
return false;
|
||||
}
|
||||
// If there are less characters than expected before the SEPARATOR.
|
||||
if (code.indexOf(SEPARATOR_) >= 0 &&
|
||||
code.indexOf(SEPARATOR_) < SEPARATOR_POSITION_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if a code is a valid full Open Location Code.
|
||||
*
|
||||
* @param {string} code The string to check.
|
||||
* @return {boolean} True if the code represents a valid latitude and
|
||||
* longitude combination.
|
||||
*/
|
||||
var isFull = OpenLocationCode.isFull = function(code) {
|
||||
if (!isValid(code)) {
|
||||
return false;
|
||||
}
|
||||
// If it's short, it's not full.
|
||||
if (isShort(code)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Work out what the first latitude character indicates for latitude.
|
||||
var firstLatValue = CODE_ALPHABET_.indexOf(
|
||||
code.charAt(0).toUpperCase()) * ENCODING_BASE_;
|
||||
if (firstLatValue >= LATITUDE_MAX_ * 2) {
|
||||
// The code would decode to a latitude of >= 90 degrees.
|
||||
return false;
|
||||
}
|
||||
if (code.length > 1) {
|
||||
// Work out what the first longitude character indicates for longitude.
|
||||
var firstLngValue = CODE_ALPHABET_.indexOf(
|
||||
code.charAt(1).toUpperCase()) * ENCODING_BASE_;
|
||||
if (firstLngValue >= LONGITUDE_MAX_ * 2) {
|
||||
// The code would decode to a longitude of >= 180 degrees.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a location into an Open Location Code.
|
||||
*
|
||||
* @param {number} latitude The latitude in signed decimal degrees. It will
|
||||
* be clipped to the range -90 to 90.
|
||||
* @param {number} longitude The longitude in signed decimal degrees. Will be
|
||||
* normalised to the range -180 to 180.
|
||||
* @param {?number} codeLength The length of the code to generate. If
|
||||
* omitted, the value OpenLocationCode.CODE_PRECISION_NORMAL will be used.
|
||||
* For a more precise result, OpenLocationCode.CODE_PRECISION_EXTRA is
|
||||
* recommended.
|
||||
* @return {string} The code.
|
||||
* @throws {Exception} if any of the input values are not numbers.
|
||||
*/
|
||||
var encode = OpenLocationCode.encode = function(latitude,
|
||||
longitude, codeLength) {
|
||||
latitude = Number(latitude);
|
||||
longitude = Number(longitude);
|
||||
if (typeof codeLength == 'undefined') {
|
||||
codeLength = OpenLocationCode.CODE_PRECISION_NORMAL;
|
||||
} else {
|
||||
codeLength = Math.min(MAX_DIGIT_COUNT_, Number(codeLength));
|
||||
}
|
||||
if (isNaN(latitude) || isNaN(longitude) || isNaN(codeLength)) {
|
||||
throw new Error('ValueError: Parameters are not numbers');
|
||||
}
|
||||
if (codeLength < 2 ||
|
||||
(codeLength < PAIR_CODE_LENGTH_ && codeLength % 2 == 1)) {
|
||||
throw new Error('IllegalArgumentException: Invalid Open Location Code length');
|
||||
}
|
||||
// Ensure that latitude and longitude are valid.
|
||||
latitude = clipLatitude(latitude);
|
||||
longitude = normalizeLongitude(longitude);
|
||||
// Latitude 90 needs to be adjusted to be just less, so the returned code
|
||||
// can also be decoded.
|
||||
if (latitude == 90) {
|
||||
latitude = latitude - computeLatitudePrecision(codeLength);
|
||||
}
|
||||
var code = '';
|
||||
|
||||
// Compute the code.
|
||||
// This approach converts each value to an integer after multiplying it by
|
||||
// the final precision. This allows us to use only integer operations, so
|
||||
// avoiding any accumulation of floating point representation errors.
|
||||
|
||||
// Multiply values by their precision and convert to positive.
|
||||
// Force to integers so the division operations will have integer results.
|
||||
// Note: JavaScript requires rounding before truncating to ensure precision!
|
||||
var latVal =
|
||||
Math.floor(Math.round((latitude + LATITUDE_MAX_) * FINAL_LAT_PRECISION_ * 1e6) / 1e6);
|
||||
var lngVal =
|
||||
Math.floor(Math.round((longitude + LONGITUDE_MAX_) * FINAL_LNG_PRECISION_ * 1e6) / 1e6);
|
||||
|
||||
// Compute the grid part of the code if necessary.
|
||||
if (codeLength > PAIR_CODE_LENGTH_) {
|
||||
for (var i = 0; i < MAX_DIGIT_COUNT_ - PAIR_CODE_LENGTH_; i++) {
|
||||
var latDigit = latVal % GRID_ROWS_;
|
||||
var lngDigit = lngVal % GRID_COLUMNS_;
|
||||
var ndx = latDigit * GRID_COLUMNS_ + lngDigit;
|
||||
code = CODE_ALPHABET_.charAt(ndx) + code;
|
||||
// Note! Integer division.
|
||||
latVal = Math.floor(latVal / GRID_ROWS_);
|
||||
lngVal = Math.floor(lngVal / GRID_COLUMNS_);
|
||||
}
|
||||
} else {
|
||||
latVal = Math.floor(latVal / Math.pow(GRID_ROWS_, GRID_CODE_LENGTH_));
|
||||
lngVal = Math.floor(lngVal / Math.pow(GRID_COLUMNS_, GRID_CODE_LENGTH_));
|
||||
}
|
||||
// Compute the pair section of the code.
|
||||
for (var i = 0; i < PAIR_CODE_LENGTH_ / 2; i++) {
|
||||
code = CODE_ALPHABET_.charAt(lngVal % ENCODING_BASE_) + code;
|
||||
code = CODE_ALPHABET_.charAt(latVal % ENCODING_BASE_) + code;
|
||||
latVal = Math.floor(latVal / ENCODING_BASE_);
|
||||
lngVal = Math.floor(lngVal / ENCODING_BASE_);
|
||||
}
|
||||
|
||||
// Add the separator character.
|
||||
code = code.substring(0, SEPARATOR_POSITION_) +
|
||||
SEPARATOR_ +
|
||||
code.substring(SEPARATOR_POSITION_);
|
||||
|
||||
|
||||
// If we don't need to pad the code, return the requested section.
|
||||
if (codeLength >= SEPARATOR_POSITION_) {
|
||||
return code.substring(0, codeLength + 1);
|
||||
}
|
||||
// Pad and return the code.
|
||||
return code.substring(0, codeLength) +
|
||||
Array(SEPARATOR_POSITION_ - codeLength + 1).join(PADDING_CHARACTER_) + SEPARATOR_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes an Open Location Code into its location coordinates.
|
||||
*
|
||||
* Returns a CodeArea object that includes the coordinates of the bounding
|
||||
* box - the lower left, center and upper right.
|
||||
*
|
||||
* @param {string} code The code to decode.
|
||||
* @return {OpenLocationCode.CodeArea} An object with the coordinates of the
|
||||
* area of the code.
|
||||
* @throws {Exception} If the code is not valid.
|
||||
*/
|
||||
var decode = OpenLocationCode.decode = function(code) {
|
||||
// This calculates the values for the pair and grid section separately, using
|
||||
// integer arithmetic. Only at the final step are they converted to floating
|
||||
// point and combined.
|
||||
if (!isFull(code)) {
|
||||
throw new Error('IllegalArgumentException: ' +
|
||||
'Passed Open Location Code is not a valid full code: ' + code);
|
||||
}
|
||||
// Strip the '+' and '0' characters from the code and convert to upper case.
|
||||
code = code.replace('+', '').replace(/0/g, '').toLocaleUpperCase('en-US');
|
||||
|
||||
// Initialise the values for each section. We work them out as integers and
|
||||
// convert them to floats at the end.
|
||||
var normalLat = -LATITUDE_MAX_ * PAIR_PRECISION_;
|
||||
var normalLng = -LONGITUDE_MAX_ * PAIR_PRECISION_;
|
||||
var gridLat = 0;
|
||||
var gridLng = 0;
|
||||
// How many digits do we have to process?
|
||||
var digits = Math.min(code.length, PAIR_CODE_LENGTH_);
|
||||
// Define the place value for the most significant pair.
|
||||
var pv = PAIR_FIRST_PLACE_VALUE_;
|
||||
// Decode the paired digits.
|
||||
for (var i = 0; i < digits; i += 2) {
|
||||
normalLat += CODE_ALPHABET_.indexOf(code.charAt(i)) * pv;
|
||||
normalLng += CODE_ALPHABET_.indexOf(code.charAt(i + 1)) * pv;
|
||||
if (i < digits - 2) {
|
||||
pv /= ENCODING_BASE_;
|
||||
}
|
||||
}
|
||||
// Convert the place value to a float in degrees.
|
||||
var latPrecision = pv / PAIR_PRECISION_;
|
||||
var lngPrecision = pv / PAIR_PRECISION_;
|
||||
// Process any extra precision digits.
|
||||
if (code.length > PAIR_CODE_LENGTH_) {
|
||||
// Initialise the place values for the grid.
|
||||
var rowpv = GRID_LAT_FIRST_PLACE_VALUE_;
|
||||
var colpv = GRID_LNG_FIRST_PLACE_VALUE_;
|
||||
// How many digits do we have to process?
|
||||
digits = Math.min(code.length, MAX_DIGIT_COUNT_);
|
||||
for (var i = PAIR_CODE_LENGTH_; i < digits; i++) {
|
||||
var digitVal = CODE_ALPHABET_.indexOf(code.charAt(i));
|
||||
var row = Math.floor(digitVal / GRID_COLUMNS_);
|
||||
var col = digitVal % GRID_COLUMNS_;
|
||||
gridLat += row * rowpv;
|
||||
gridLng += col * colpv;
|
||||
if (i < digits - 1) {
|
||||
rowpv /= GRID_ROWS_;
|
||||
colpv /= GRID_COLUMNS_;
|
||||
}
|
||||
}
|
||||
// Adjust the precisions from the integer values to degrees.
|
||||
latPrecision = rowpv / FINAL_LAT_PRECISION_;
|
||||
lngPrecision = colpv / FINAL_LNG_PRECISION_;
|
||||
}
|
||||
// Merge the values from the normal and extra precision parts of the code.
|
||||
var lat = normalLat / PAIR_PRECISION_ + gridLat / FINAL_LAT_PRECISION_;
|
||||
var lng = normalLng / PAIR_PRECISION_ + gridLng / FINAL_LNG_PRECISION_;
|
||||
// Multiple values by 1e14, round and then divide. This reduces errors due
|
||||
// to floating point precision.
|
||||
return new CodeArea(
|
||||
Math.round(lat * 1e14) / 1e14, Math.round(lng * 1e14) / 1e14,
|
||||
Math.round((lat + latPrecision) * 1e14) / 1e14,
|
||||
Math.round((lng + lngPrecision) * 1e14) / 1e14,
|
||||
Math.min(code.length, MAX_DIGIT_COUNT_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Recover the nearest matching code to a specified location.
|
||||
*
|
||||
* Given a valid short Open Location Code this recovers the nearest matching
|
||||
* full code to the specified location.
|
||||
*
|
||||
* @param {string} shortCode A valid short code.
|
||||
* @param {number} referenceLatitude The latitude to use for the reference
|
||||
* location.
|
||||
* @param {number} referenceLongitude The longitude to use for the reference
|
||||
* location.
|
||||
* @return {string} The nearest matching full code to the reference location.
|
||||
* @throws {Exception} if the short code is not valid, or the reference
|
||||
* position values are not numbers.
|
||||
*/
|
||||
OpenLocationCode.recoverNearest = function(
|
||||
shortCode, referenceLatitude, referenceLongitude) {
|
||||
if (!isShort(shortCode)) {
|
||||
if (isFull(shortCode)) {
|
||||
return shortCode.toUpperCase();
|
||||
} else {
|
||||
throw new Error(
|
||||
'ValueError: Passed short code is not valid: ' + shortCode);
|
||||
}
|
||||
}
|
||||
referenceLatitude = Number(referenceLatitude);
|
||||
referenceLongitude = Number(referenceLongitude);
|
||||
if (isNaN(referenceLatitude) || isNaN(referenceLongitude)) {
|
||||
throw new Error('ValueError: Reference position are not numbers');
|
||||
}
|
||||
// Ensure that latitude and longitude are valid.
|
||||
referenceLatitude = clipLatitude(referenceLatitude);
|
||||
referenceLongitude = normalizeLongitude(referenceLongitude);
|
||||
|
||||
// Clean up the passed code.
|
||||
shortCode = shortCode.toUpperCase();
|
||||
// Compute the number of digits we need to recover.
|
||||
var paddingLength = SEPARATOR_POSITION_ - shortCode.indexOf(SEPARATOR_);
|
||||
// The resolution (height and width) of the padded area in degrees.
|
||||
var resolution = Math.pow(20, 2 - (paddingLength / 2));
|
||||
// Distance from the center to an edge (in degrees).
|
||||
var halfResolution = resolution / 2.0;
|
||||
|
||||
// Use the reference location to pad the supplied short code and decode it.
|
||||
var codeArea = decode(
|
||||
encode(referenceLatitude, referenceLongitude).substr(0, paddingLength)
|
||||
+ shortCode);
|
||||
// How many degrees latitude is the code from the reference? If it is more
|
||||
// than half the resolution, we need to move it north or south but keep it
|
||||
// within -90 to 90 degrees.
|
||||
if (referenceLatitude + halfResolution < codeArea.latitudeCenter &&
|
||||
codeArea.latitudeCenter - resolution >= -LATITUDE_MAX_) {
|
||||
// If the proposed code is more than half a cell north of the reference location,
|
||||
// it's too far, and the best match will be one cell south.
|
||||
codeArea.latitudeCenter -= resolution;
|
||||
} else if (referenceLatitude - halfResolution > codeArea.latitudeCenter &&
|
||||
codeArea.latitudeCenter + resolution <= LATITUDE_MAX_) {
|
||||
// If the proposed code is more than half a cell south of the reference location,
|
||||
// it's too far, and the best match will be one cell north.
|
||||
codeArea.latitudeCenter += resolution;
|
||||
}
|
||||
|
||||
// How many degrees longitude is the code from the reference?
|
||||
if (referenceLongitude + halfResolution < codeArea.longitudeCenter) {
|
||||
codeArea.longitudeCenter -= resolution;
|
||||
} else if (referenceLongitude - halfResolution > codeArea.longitudeCenter) {
|
||||
codeArea.longitudeCenter += resolution;
|
||||
}
|
||||
|
||||
return encode(
|
||||
codeArea.latitudeCenter, codeArea.longitudeCenter, codeArea.codeLength);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove characters from the start of an OLC code.
|
||||
*
|
||||
* This uses a reference location to determine how many initial characters
|
||||
* can be removed from the OLC code. The number of characters that can be
|
||||
* removed depends on the distance between the code center and the reference
|
||||
* location.
|
||||
*
|
||||
* @param {string} code The full code to shorten.
|
||||
* @param {number} latitude The latitude to use for the reference location.
|
||||
* @param {number} longitude The longitude to use for the reference location.
|
||||
* @return {string} The code, shortened as much as possible that it is still
|
||||
* the closest matching code to the reference location.
|
||||
* @throws {Exception} if the passed code is not a valid full code or the
|
||||
* reference location values are not numbers.
|
||||
*/
|
||||
OpenLocationCode.shorten = function(
|
||||
code, latitude, longitude) {
|
||||
if (!isFull(code)) {
|
||||
throw new Error('ValueError: Passed code is not valid and full: ' + code);
|
||||
}
|
||||
if (code.indexOf(PADDING_CHARACTER_) != -1) {
|
||||
throw new Error('ValueError: Cannot shorten padded codes: ' + code);
|
||||
}
|
||||
code = code.toUpperCase();
|
||||
var codeArea = decode(code);
|
||||
if (codeArea.codeLength < MIN_TRIMMABLE_CODE_LEN_) {
|
||||
throw new Error(
|
||||
'ValueError: Code length must be at least ' +
|
||||
MIN_TRIMMABLE_CODE_LEN_);
|
||||
}
|
||||
// Ensure that latitude and longitude are valid.
|
||||
latitude = Number(latitude);
|
||||
longitude = Number(longitude);
|
||||
if (isNaN(latitude) || isNaN(longitude)) {
|
||||
throw new Error('ValueError: Reference position are not numbers');
|
||||
}
|
||||
latitude = clipLatitude(latitude);
|
||||
longitude = normalizeLongitude(longitude);
|
||||
// How close are the latitude and longitude to the code center.
|
||||
var range = Math.max(
|
||||
Math.abs(codeArea.latitudeCenter - latitude),
|
||||
Math.abs(codeArea.longitudeCenter - longitude));
|
||||
for (var i = PAIR_RESOLUTIONS_.length - 2; i >= 1; i--) {
|
||||
// Check if we're close enough to shorten. The range must be less than 1/2
|
||||
// the resolution to shorten at all, and we want to allow some safety, so
|
||||
// use 0.3 instead of 0.5 as a multiplier.
|
||||
if (range < (PAIR_RESOLUTIONS_[i] * 0.3)) {
|
||||
// Trim it.
|
||||
return code.substring((i + 1) * 2);
|
||||
}
|
||||
}
|
||||
return code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clip a latitude into the range -90 to 90.
|
||||
*
|
||||
* @param {number} latitude
|
||||
* @return {number} The latitude value clipped to be in the range.
|
||||
*/
|
||||
var clipLatitude = function(latitude) {
|
||||
return Math.min(90, Math.max(-90, latitude));
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the latitude precision value for a given code length.
|
||||
* Lengths <= 10 have the same precision for latitude and longitude, but
|
||||
* lengths > 10 have different precisions due to the grid method having
|
||||
* fewer columns than rows.
|
||||
* @param {number} codeLength
|
||||
* @return {number} The latitude precision in degrees.
|
||||
*/
|
||||
var computeLatitudePrecision = function(codeLength) {
|
||||
if (codeLength <= 10) {
|
||||
return Math.pow(ENCODING_BASE_, Math.floor(codeLength / -2 + 2));
|
||||
}
|
||||
return Math.pow(ENCODING_BASE_, -3) / Math.pow(GRID_ROWS_, codeLength - 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize a longitude into the range -180 to 180, not including 180.
|
||||
*
|
||||
* @param {number} longitude
|
||||
* @return {number} Normalized into the range -180 to 180.
|
||||
*/
|
||||
var normalizeLongitude = function(longitude) {
|
||||
while (longitude < -180) {
|
||||
longitude = longitude + 360;
|
||||
}
|
||||
while (longitude >= 180) {
|
||||
longitude = longitude - 360;
|
||||
}
|
||||
return longitude;
|
||||
};
|
||||
|
||||
/**
|
||||
* Coordinates of a decoded Open Location Code.
|
||||
*
|
||||
* The coordinates include the latitude and longitude of the lower left and
|
||||
* upper right corners and the center of the bounding box for the area the
|
||||
* code represents.
|
||||
* @param {number} latitudeLo
|
||||
* @param {number} longitudeLo
|
||||
* @param {number} latitudeHi
|
||||
* @param {number} longitudeHi
|
||||
* @param {number} codeLength
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var CodeArea = OpenLocationCode.CodeArea = function(
|
||||
latitudeLo, longitudeLo, latitudeHi, longitudeHi, codeLength) {
|
||||
return new OpenLocationCode.CodeArea.fn.Init(
|
||||
latitudeLo, longitudeLo, latitudeHi, longitudeHi, codeLength);
|
||||
};
|
||||
CodeArea.fn = CodeArea.prototype = {
|
||||
Init: function(
|
||||
latitudeLo, longitudeLo, latitudeHi, longitudeHi, codeLength) {
|
||||
/**
|
||||
* The latitude of the SW corner.
|
||||
* @type {number}
|
||||
*/
|
||||
this.latitudeLo = latitudeLo;
|
||||
/**
|
||||
* The longitude of the SW corner in degrees.
|
||||
* @type {number}
|
||||
*/
|
||||
this.longitudeLo = longitudeLo;
|
||||
/**
|
||||
* The latitude of the NE corner in degrees.
|
||||
* @type {number}
|
||||
*/
|
||||
this.latitudeHi = latitudeHi;
|
||||
/**
|
||||
* The longitude of the NE corner in degrees.
|
||||
* @type {number}
|
||||
*/
|
||||
this.longitudeHi = longitudeHi;
|
||||
/**
|
||||
* The number of digits in the code.
|
||||
* @type {number}
|
||||
*/
|
||||
this.codeLength = codeLength;
|
||||
/**
|
||||
* The latitude of the center in degrees.
|
||||
* @type {number}
|
||||
*/
|
||||
this.latitudeCenter = Math.min(
|
||||
latitudeLo + (latitudeHi - latitudeLo) / 2, LATITUDE_MAX_);
|
||||
/**
|
||||
* The longitude of the center in degrees.
|
||||
* @type {number}
|
||||
*/
|
||||
this.longitudeCenter = Math.min(
|
||||
longitudeLo + (longitudeHi - longitudeLo) / 2, LONGITUDE_MAX_);
|
||||
},
|
||||
};
|
||||
CodeArea.fn.Init.prototype = CodeArea.fn;
|
||||
|
||||
return OpenLocationCode;
|
||||
}));
|
1
plugins/tiddlywiki/geospatial/files/open-location-code/openlocationcode.min.js
vendored
Normal file
1
plugins/tiddlywiki/geospatial/files/open-location-code/openlocationcode.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(["b"],function(t){return e.returnExportsGlobal=r()}):"object"==typeof module&&module.exports?module.exports=r(require("b")):e.OpenLocationCode=r()}(this,function(){var e={CODE_PRECISION_NORMAL:10,CODE_PRECISION_EXTRA:11},r="23456789CFGHJMPQRVWX",t=r.length,n=Math.pow(t,4),o=Math.pow(t,3),i=[20,1,.05,.0025,125e-6],a=Math.pow(5,4),u=Math.pow(4,4),f=o*Math.pow(5,5),h=o*Math.pow(4,5);e.getAlphabet=function(){return r};var l=e.isValid=function(e){if(!e||"string"!=typeof e)return!1;if(-1==e.indexOf("+"))return!1;if(e.indexOf("+")!=e.lastIndexOf("+"))return!1;if(1==e.length)return!1;if(e.indexOf("+")>8||e.indexOf("+")%2==1)return!1;if(e.indexOf("0")>-1){if(e.indexOf("+")<8)return!1;if(0==e.indexOf("0"))return!1;var t=e.match(new RegExp("(0+)","g"));if(t.length>1||t[0].length%2==1||t[0].length>6)return!1;if("+"!=e.charAt(e.length-1))return!1}if(e.length-e.indexOf("+")-1==1)return!1;for(var n=0,o=(e=e.replace(new RegExp("\\++"),"").replace(new RegExp("0+"),"")).length;n<o;n++){var i=e.charAt(n).toUpperCase();if("+"!=i&&-1==r.indexOf(i))return!1}return!0},d=e.isShort=function(e){return!!l(e)&&(e.indexOf("+")>=0&&e.indexOf("+")<8)},s=e.isFull=function(e){if(!l(e))return!1;if(d(e))return!1;if(r.indexOf(e.charAt(0).toUpperCase())*t>=180)return!1;if(e.length>1&&r.indexOf(e.charAt(1).toUpperCase())*t>=360)return!1;return!0},p=e.encode=function(n,o,i){if(n=Number(n),o=Number(o),i=void 0===i?e.CODE_PRECISION_NORMAL:Math.min(15,Number(i)),isNaN(n)||isNaN(o)||isNaN(i))throw new Error("ValueError: Parameters are not numbers");if(i<2||i<10&&i%2==1)throw new Error("IllegalArgumentException: Invalid Open Location Code length");n=g(n),o=C(o),90==n&&(n-=M(i));var a="",u=Math.floor(Math.round((n+90)*f*1e6)/1e6),l=Math.floor(Math.round((o+180)*h*1e6)/1e6);if(i>10)for(var d=0;d<5;d++){a=r.charAt(4*(u%5)+l%4)+a,u=Math.floor(u/5),l=Math.floor(l/4)}else u=Math.floor(u/Math.pow(5,5)),l=Math.floor(l/Math.pow(4,5));for(d=0;d<5;d++)a=r.charAt(l%t)+a,a=r.charAt(u%t)+a,u=Math.floor(u/t),l=Math.floor(l/t);return a=a.substring(0,8)+"+"+a.substring(8),i>=8?a.substring(0,i+1):a.substring(0,i)+Array(8-i+1).join("0")+"+"},c=e.decode=function(e){if(!s(e))throw new Error("IllegalArgumentException: Passed Open Location Code is not a valid full code: "+e);e=e.replace("+","").replace(/0/g,"").toLocaleUpperCase("en-US");for(var i=-90*o,l=-180*o,d=0,p=0,c=Math.min(e.length,10),g=n,M=0;M<c;M+=2)i+=r.indexOf(e.charAt(M))*g,l+=r.indexOf(e.charAt(M+1))*g,M<c-2&&(g/=t);var C=g/o,O=g/o;if(e.length>10){var x=a,E=u;c=Math.min(e.length,15);for(M=10;M<c;M++){var m=r.indexOf(e.charAt(M));d+=Math.floor(m/4)*x,p+=m%4*E,M<c-1&&(x/=5,E/=4)}C=x/f,O=E/h}var N=i/o+d/f,b=l/o+p/h;return new w(Math.round(1e14*N)/1e14,Math.round(1e14*b)/1e14,Math.round(1e14*(N+C))/1e14,Math.round(1e14*(b+O))/1e14,Math.min(e.length,15))};e.recoverNearest=function(e,r,t){if(!d(e)){if(s(e))return e.toUpperCase();throw new Error("ValueError: Passed short code is not valid: "+e)}if(r=Number(r),t=Number(t),isNaN(r)||isNaN(t))throw new Error("ValueError: Reference position are not numbers");r=g(r),t=C(t);var n=8-(e=e.toUpperCase()).indexOf("+"),o=Math.pow(20,2-n/2),i=o/2,a=c(p(r,t).substr(0,n)+e);return r+i<a.latitudeCenter&&a.latitudeCenter-o>=-90?a.latitudeCenter-=o:r-i>a.latitudeCenter&&a.latitudeCenter+o<=90&&(a.latitudeCenter+=o),t+i<a.longitudeCenter?a.longitudeCenter-=o:t-i>a.longitudeCenter&&(a.longitudeCenter+=o),p(a.latitudeCenter,a.longitudeCenter,a.codeLength)},e.shorten=function(e,r,t){if(!s(e))throw new Error("ValueError: Passed code is not valid and full: "+e);if(-1!=e.indexOf("0"))throw new Error("ValueError: Cannot shorten padded codes: "+e);e=e.toUpperCase();var n=c(e);if(n.codeLength<6)throw new Error("ValueError: Code length must be at least 6");if(r=Number(r),t=Number(t),isNaN(r)||isNaN(t))throw new Error("ValueError: Reference position are not numbers");r=g(r),t=C(t);for(var o=Math.max(Math.abs(n.latitudeCenter-r),Math.abs(n.longitudeCenter-t)),a=i.length-2;a>=1;a--)if(o<.3*i[a])return e.substring(2*(a+1));return e};var g=function(e){return Math.min(90,Math.max(-90,e))},M=function(e){return e<=10?Math.pow(t,Math.floor(e/-2+2)):Math.pow(t,-3)/Math.pow(5,e-10)},C=function(e){for(;e<-180;)e+=360;for(;e>=180;)e-=360;return e},w=e.CodeArea=function(r,t,n,o,i){return new e.CodeArea.fn.Init(r,t,n,o,i)};return w.fn=w.prototype={Init:function(e,r,t,n,o){this.latitudeLo=e,this.longitudeLo=r,this.latitudeHi=t,this.longitudeHi=n,this.codeLength=o,this.latitudeCenter=Math.min(e+(t-e)/2,90),this.longitudeCenter=Math.min(r+(n-r)/2,180)}},w.fn.Init.prototype=w.fn,e});
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"tiddlers": [
|
||||
{
|
||||
"file": "openlocationcode.js",
|
||||
"fields": {
|
||||
"type": "application/javascript",
|
||||
"title": "$:/plugins/tiddlywiki/geospatial/openlocationcode.js",
|
||||
"module-type": "library"
|
||||
},
|
||||
"prefix": "(function(require){",
|
||||
"suffix": "})(function(){});"
|
||||
},
|
||||
{
|
||||
"file": "LICENSE",
|
||||
"fields": {
|
||||
"type": "text/plain",
|
||||
"title": "$:/plugins/tiddlywiki/geospatial/openlocationcode/LICENSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -15,9 +15,9 @@ Filter operators for geospatial helpers
|
||||
var turf = require("$:/plugins/tiddlywiki/geospatial/turf.js");
|
||||
|
||||
exports.geopoint = function(source,operator,options) {
|
||||
var lat = $tw.utils.parseNumber(operator.operands[0] || 0),
|
||||
long = $tw.utils.parseNumber(operator.operands[1] || 0),
|
||||
alt = $tw.utils.parseNumber(operator.operands[2] || 0);
|
||||
var lat = $tw.utils.parseNumber(operator.operands[0] || "0"),
|
||||
long = $tw.utils.parseNumber(operator.operands[1] || "0"),
|
||||
alt = $tw.utils.parseNumber(operator.operands[2] || "0");
|
||||
return [JSON.stringify(turf.point([lat,long,alt]))];
|
||||
};
|
||||
|
||||
|
55
plugins/tiddlywiki/geospatial/operators/olc.js
Normal file
55
plugins/tiddlywiki/geospatial/operators/olc.js
Normal file
@ -0,0 +1,55 @@
|
||||
/*\
|
||||
title: $:/plugins/tiddlywiki/geospatial/operators/olc.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
Filter operators for open location code conversions
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var openlocationcode = require("$:/plugins/tiddlywiki/geospatial/openlocationcode.js"),
|
||||
turf = require("$:/plugins/tiddlywiki/geospatial/turf.js"),
|
||||
geotools = require("$:/plugins/tiddlywiki/geospatial/geotools.js");
|
||||
|
||||
exports["olc-decode"] = function(source,operator,options) {
|
||||
var olc;
|
||||
try {
|
||||
olc = openlocationcode.decode(operator.operands[0] || "")
|
||||
} catch(e) {
|
||||
return [];
|
||||
}
|
||||
var suffixes = (operator.suffixes || [])[0] || [],
|
||||
obj;
|
||||
if(suffixes.indexOf("bounds") !== -1) {
|
||||
obj = turf.polygon([[
|
||||
[olc.latitudeLo, olc.longitudeLo],
|
||||
[olc.latitudeHi, olc.longitudeLo],
|
||||
[olc.latitudeHi, olc.longitudeHi],
|
||||
[olc.latitudeLo, olc.longitudeHi],
|
||||
[olc.latitudeLo, olc.longitudeLo]
|
||||
]]);
|
||||
} else {
|
||||
obj = turf.point([olc.latitudeCenter,olc.longitudeCenter]);
|
||||
}
|
||||
return [JSON.stringify(obj)];
|
||||
};
|
||||
|
||||
exports["olc-encode"] = function(source,operator,options) {
|
||||
var lat = $tw.utils.parseNumber(operator.operands[0] || "0"),
|
||||
long = $tw.utils.parseNumber(operator.operands[1] || "0"),
|
||||
codelength = $tw.utils.parseNumber(operator.operands[2] || "0") || openlocationcode.CODE_PRECISION_NORMAL,
|
||||
olc;
|
||||
try {
|
||||
olc = openlocationcode.encode(lat,long,codelength);
|
||||
} catch(e) {
|
||||
return []
|
||||
}
|
||||
return [olc];
|
||||
};
|
||||
|
||||
})();
|
@ -18,6 +18,7 @@ exports.after = ["load-modules"];
|
||||
exports.synchronous = true;
|
||||
|
||||
exports.startup = function() {
|
||||
// var openlocationcode = require("$:/plugins/tiddlywiki/geospatial/openlocationcode.js");
|
||||
// var turf = require("$:/plugins/tiddlywiki/geospatial/turf.js");
|
||||
// var leaflet = require("$:/plugins/tiddlywiki/geospatial/leaflet.js");
|
||||
};
|
||||
|
@ -0,0 +1,20 @@
|
||||
title: $:/plugins/tiddlywiki/geospatial/tests/operators/olc
|
||||
description: olc operators
|
||||
type: text/vnd.tiddlywiki-multiple
|
||||
tags: [[$:/tags/wiki-test-spec]]
|
||||
|
||||
title: Output
|
||||
|
||||
(<$text text={{{ [olc-encode[51.751944],[-1.257778]] }}}/>)
|
||||
(<$text text={{{ [olc-encode[51.751944],[-1.257778],[11]] }}}/>)
|
||||
(<$text text={{{ [olc-decode:bounds[9C3WQP2R+QV]] }}}/>)
|
||||
(<$text text={{{ [olc-decode:point[9C3WQP2R+QVH]] }}}/>)
|
||||
|
||||
+
|
||||
title: ExpectedResult
|
||||
|
||||
<p>(9C3WQP2R+QV)
|
||||
(9C3WQP2R+QVH)
|
||||
({"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[51.751875,-1.257875],[51.752,-1.257875],[51.752,-1.25775],[51.751875,-1.25775],[51.751875,-1.257875]]]}})
|
||||
({"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[51.7519375,-1.257765625]}})
|
||||
</p>
|
Loading…
Reference in New Issue
Block a user