aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShinigami <[email protected]>2022-01-14 18:41:58 +0100
committerDamien Retzinger <[email protected]>2022-01-14 18:37:49 -0500
commit88afa60f739a60f08efe7365a3b80dbfd306055a (patch)
tree85c607aa2b75a6da5314b4979897bcc8ff2be981 /src
parenta1208cacf76f96738a3505e2a71ae91d9759b805 (diff)
downloadfaker-88afa60f739a60f08efe7365a3b80dbfd306055a.tar.xz
faker-88afa60f739a60f08efe7365a3b80dbfd306055a.zip
feat: migrate address (#133)
Diffstat (limited to 'src')
-rw-r--r--src/address.ts482
-rw-r--r--src/fake.ts2
-rw-r--r--src/helpers.ts4
-rw-r--r--src/index.ts3
4 files changed, 487 insertions, 4 deletions
diff --git a/src/address.ts b/src/address.ts
new file mode 100644
index 00000000..540da206
--- /dev/null
+++ b/src/address.ts
@@ -0,0 +1,482 @@
+import type { Faker } from '.';
+import type { Fake } from './fake';
+import type { Helpers } from './helpers';
+
+let f: Fake['fake'];
+
+export class Address {
+ readonly Helpers: Helpers;
+
+ constructor(private readonly faker: Faker) {
+ f = this.faker.fake;
+ this.Helpers = this.faker.helpers;
+
+ // Bind `this` so namespaced is working correctly
+ for (const name of Object.getOwnPropertyNames(Address.prototype)) {
+ if (name === 'constructor' || typeof this[name] !== 'function') {
+ continue;
+ }
+ this[name] = this[name].bind(this);
+ }
+
+ // TODO @Shinigami92 2022-01-13: Need better strategy
+ // @ts-expect-error
+ this.direction.schema = {
+ description:
+ 'Generates a direction. Use optional useAbbr bool to return abbreviation',
+ sampleResults: ['Northwest', 'South', 'SW', 'E'],
+ };
+ // @ts-expect-error
+ this.cardinalDirection.schema = {
+ description:
+ 'Generates a cardinal direction. Use optional useAbbr boolean to return abbreviation',
+ sampleResults: ['North', 'South', 'E', 'W'],
+ };
+ // @ts-expect-error
+ this.ordinalDirection.schema = {
+ description:
+ 'Generates an ordinal direction. Use optional useAbbr boolean to return abbreviation',
+ sampleResults: ['Northwest', 'Southeast', 'SW', 'NE'],
+ };
+ }
+
+ /**
+ * Generates random zipcode from format. If format is not specified, the
+ * locale's zip format is used.
+ *
+ * @method faker.address.zipCode
+ * @param format
+ */
+ zipCode(format?: string) {
+ // if zip format is not specified, use the zip format defined for the locale
+ if (typeof format === 'undefined') {
+ const localeFormat = this.faker.definitions.address.postcode;
+ if (typeof localeFormat === 'string') {
+ format = localeFormat;
+ } else {
+ format = this.faker.random.arrayElement(localeFormat);
+ }
+ }
+ return this.Helpers.replaceSymbols(format);
+ }
+
+ /**
+ * Generates random zipcode from state abbreviation. If state abbreviation is
+ * not specified, a random zip code is generated according to the locale's zip format.
+ * Only works for locales with postcode_by_state definition. If a locale does not
+ * have a postcode_by_state definition, a random zip code is generated according
+ * to the locale's zip format.
+ *
+ * @method faker.address.zipCodeByState
+ * @param state
+ */
+ zipCodeByState(state: string) {
+ const zipRange = this.faker.definitions.address.postcode_by_state[state];
+ if (zipRange) {
+ return this.faker.datatype.number(zipRange);
+ }
+ return this.faker.address.zipCode();
+ }
+
+ /**
+ * Generates a random localized city name. The format string can contain any
+ * method provided by faker wrapped in `{{}}`, e.g. `{{name.firstName}}` in
+ * order to build the city name.
+ *
+ * If no format string is provided one of the following is randomly used:
+ *
+ * * `{{address.cityPrefix}} {{name.firstName}}{{address.citySuffix}}`
+ * * `{{address.cityPrefix}} {{name.firstName}}`
+ * * `{{name.firstName}}{{address.citySuffix}}`
+ * * `{{name.lastName}}{{address.citySuffix}}`
+ * * `{{address.cityName}}` when city name is available
+ *
+ * @method faker.address.city
+ * @param format
+ */
+ city(format?: string | number) {
+ const formats = [
+ '{{address.cityPrefix}} {{name.firstName}}{{address.citySuffix}}',
+ '{{address.cityPrefix}} {{name.firstName}}',
+ '{{name.firstName}}{{address.citySuffix}}',
+ '{{name.lastName}}{{address.citySuffix}}',
+ ];
+
+ if (!format && this.faker.definitions.address.city_name) {
+ formats.push('{{address.cityName}}');
+ }
+
+ if (typeof format !== 'number') {
+ format = this.faker.datatype.number(formats.length - 1);
+ }
+
+ return f(formats[format]);
+ }
+
+ /**
+ * Return a random localized city prefix
+ *
+ * @method faker.address.cityPrefix
+ */
+ cityPrefix(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.city_prefix
+ );
+ }
+
+ /**
+ * Return a random localized city suffix
+ *
+ * @method faker.address.citySuffix
+ */
+ citySuffix(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.city_suffix
+ );
+ }
+
+ /**
+ * Returns a random city name
+ *
+ * @method faker.address.cityName
+ */
+ cityName(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.city_name
+ );
+ }
+
+ /**
+ * Returns a random localized street name
+ *
+ * @method faker.address.streetName
+ */
+ streetName(): string {
+ let result: string;
+ let suffix = this.faker.address.streetSuffix();
+ if (suffix !== '') {
+ suffix = ' ' + suffix;
+ }
+
+ switch (this.faker.datatype.number(1)) {
+ case 0:
+ result = this.faker.name.lastName() + suffix;
+ break;
+ case 1:
+ result = this.faker.name.firstName() + suffix;
+ break;
+ }
+ return result;
+ }
+
+ //
+ // TODO: change all these methods that accept a boolean to instead accept an options hash.
+ //
+
+ /**
+ * Returns a random localized street address
+ *
+ * @method faker.address.streetAddress
+ * @param useFullAddress
+ */
+ streetAddress(useFullAddress: boolean = false): string {
+ let address = '';
+ switch (this.faker.datatype.number(2)) {
+ case 0:
+ address =
+ this.Helpers.replaceSymbolWithNumber('#####') +
+ ' ' +
+ this.faker.address.streetName();
+ break;
+ case 1:
+ address =
+ this.Helpers.replaceSymbolWithNumber('####') +
+ ' ' +
+ this.faker.address.streetName();
+ break;
+ case 2:
+ address =
+ this.Helpers.replaceSymbolWithNumber('###') +
+ ' ' +
+ this.faker.address.streetName();
+ break;
+ }
+ return useFullAddress
+ ? address + ' ' + this.faker.address.secondaryAddress()
+ : address;
+ }
+
+ /**
+ * streetSuffix
+ *
+ * @method faker.address.streetSuffix
+ */
+ streetSuffix(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.street_suffix
+ );
+ }
+
+ /**
+ * streetPrefix
+ *
+ * @method faker.address.streetPrefix
+ */
+ streetPrefix(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.street_prefix
+ );
+ }
+
+ /**
+ * secondaryAddress
+ *
+ * @method faker.address.secondaryAddress
+ */
+ secondaryAddress() {
+ return this.Helpers.replaceSymbolWithNumber(
+ this.faker.random.arrayElement(['Apt. ###', 'Suite ###'])
+ );
+ }
+
+ /**
+ * county
+ *
+ * @method faker.address.county
+ */
+ county(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.county
+ );
+ }
+
+ /**
+ * country
+ *
+ * @method faker.address.country
+ */
+ country(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.country
+ );
+ }
+
+ /**
+ * countryCode
+ *
+ * @method faker.address.countryCode
+ * @param alphaCode default alpha-2
+ */
+ countryCode(alphaCode: string = 'alpha-2'): string {
+ if (alphaCode === 'alpha-2') {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.country_code
+ );
+ }
+
+ if (alphaCode === 'alpha-3') {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.country_code_alpha_3
+ );
+ }
+
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.country_code
+ );
+ }
+
+ /**
+ * state
+ *
+ * @method faker.address.state
+ * @param useAbbr
+ */
+ // TODO christopher 2022-01-13: useAbbr not in use
+ state(useAbbr?: boolean): string {
+ return this.faker.random.arrayElement(this.faker.definitions.address.state);
+ }
+
+ /**
+ * stateAbbr
+ *
+ * @method faker.address.stateAbbr
+ */
+ stateAbbr(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.state_abbr
+ );
+ }
+
+ /**
+ * latitude
+ *
+ * @method faker.address.latitude
+ * @param max default is 90
+ * @param min default is -90
+ * @param precision default is 4
+ */
+ latitude(max: number = 90, min: number = -90, precision: number = 4): string {
+ return this.faker.datatype
+ .number({
+ max: max,
+ min: min,
+ precision: parseFloat((0.0).toPrecision(precision) + '1'),
+ })
+ .toFixed(precision);
+ }
+
+ /**
+ * longitude
+ *
+ * @method faker.address.longitude
+ * @param max default is 180
+ * @param min default is -180
+ * @param precision default is 4
+ */
+ longitude(
+ max: number = 180,
+ min: number = -180,
+ precision: number = 4
+ ): string {
+ return this.faker.datatype
+ .number({
+ max: max,
+ min: min,
+ precision: parseFloat((0.0).toPrecision(precision) + '1'),
+ })
+ .toFixed(precision);
+ }
+
+ /**
+ * direction
+ *
+ * @method faker.address.direction
+ * @param useAbbr return direction abbreviation. defaults to false
+ */
+ direction(useAbbr: boolean = false) {
+ if (!useAbbr) {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction
+ );
+ }
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction_abbr
+ );
+ }
+
+ /**
+ * cardinal direction
+ *
+ * @method faker.address.cardinalDirection
+ * @param useAbbr return direction abbreviation. defaults to false
+ */
+ cardinalDirection(useAbbr: boolean = false): string {
+ if (!useAbbr) {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction.slice(0, 4)
+ );
+ }
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction_abbr.slice(0, 4)
+ );
+ }
+
+ /**
+ * ordinal direction
+ *
+ * @method faker.address.ordinalDirection
+ * @param useAbbr return direction abbreviation. defaults to false
+ */
+ ordinalDirection(useAbbr: boolean = false): string {
+ if (!useAbbr) {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction.slice(4, 8)
+ );
+ }
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.direction_abbr.slice(4, 8)
+ );
+ }
+
+ nearbyGPSCoordinate(
+ coordinate?: number[],
+ radius?: number,
+ isMetric?: boolean
+ ): string[] {
+ function randomFloat(min: number, max: number): number {
+ return Math.random() * (max - min) + min;
+ }
+ function degreesToRadians(degrees: number): number {
+ return degrees * (Math.PI / 180.0);
+ }
+ function radiansToDegrees(radians: number): number {
+ return radians * (180.0 / Math.PI);
+ }
+ function kilometersToMiles(miles: number): number {
+ return miles * 0.621371;
+ }
+ function coordinateWithOffset(
+ coordinate: number[],
+ bearing: number,
+ distance: number,
+ isMetric: boolean
+ ): number[] {
+ const R = 6378.137; // Radius of the Earth (http://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html)
+ const d = isMetric ? distance : kilometersToMiles(distance); // Distance in km
+
+ const lat1 = degreesToRadians(coordinate[0]); //Current lat point converted to radians
+ const lon1 = degreesToRadians(coordinate[1]); //Current long point converted to radians
+
+ const lat2 = Math.asin(
+ Math.sin(lat1) * Math.cos(d / R) +
+ Math.cos(lat1) * Math.sin(d / R) * Math.cos(bearing)
+ );
+
+ let lon2 =
+ lon1 +
+ Math.atan2(
+ Math.sin(bearing) * Math.sin(d / R) * Math.cos(lat1),
+ Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2)
+ );
+
+ // Keep longitude in range [-180, 180]
+ if (lon2 > degreesToRadians(180)) {
+ lon2 = lon2 - degreesToRadians(360);
+ } else if (lon2 < degreesToRadians(-180)) {
+ lon2 = lon2 + degreesToRadians(360);
+ }
+
+ return [radiansToDegrees(lat2), radiansToDegrees(lon2)];
+ }
+
+ // If there is no coordinate, the best we can do is return a random GPS coordinate.
+ if (coordinate === undefined) {
+ return [this.faker.address.latitude(), this.faker.address.longitude()];
+ }
+ radius ||= 10.0;
+ isMetric ||= false;
+
+ // TODO: implement either a gaussian/uniform distribution of points in circular region.
+ // Possibly include param to function that allows user to choose between distributions.
+
+ // This approach will likely result in a higher density of points near the center.
+ const randomCoord = coordinateWithOffset(
+ coordinate,
+ degreesToRadians(Math.random() * 360.0),
+ radius,
+ isMetric
+ );
+ return [randomCoord[0].toFixed(4), randomCoord[1].toFixed(4)];
+ }
+
+ /**
+ * Return a random time zone
+ *
+ * @method faker.address.timeZone
+ */
+ timeZone(): string {
+ return this.faker.random.arrayElement(
+ this.faker.definitions.address.time_zone
+ );
+ }
+}
diff --git a/src/fake.ts b/src/fake.ts
index e435ce89..1845dbc1 100644
--- a/src/fake.ts
+++ b/src/fake.ts
@@ -79,7 +79,7 @@ export class Fake {
}
// assign the function from the module.function namespace
- const fn = this.faker[parts[0]][parts[1]];
+ const fn: Function = this.faker[parts[0]][parts[1]];
// If parameters are populated here, they are always going to be of string type
// since we might actually be dealing with an object or array,
diff --git a/src/helpers.ts b/src/helpers.ts
index 6d6aa62f..f8219b35 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -394,7 +394,7 @@ export class Helpers {
),
phone: this.faker.phone.phoneNumber(),
address: {
- street: this.faker.address.streetName(true),
+ street: this.faker.address.streetName(),
suite: this.faker.address.secondaryAddress(),
city: this.faker.address.city(),
zipcode: this.faker.address.zipCode(),
@@ -423,7 +423,7 @@ export class Helpers {
username: this.faker.internet.userName(),
email: this.faker.internet.email(),
address: {
- street: this.faker.address.streetName(true),
+ street: this.faker.address.streetName(),
suite: this.faker.address.secondaryAddress(),
city: this.faker.address.city(),
zipcode: this.faker.address.zipCode(),
diff --git a/src/index.ts b/src/index.ts
index 65dc6225..16a3b347 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,4 @@
+import { Address } from './address';
import { Animal } from './animal';
import { Commerce } from './commerce';
import { Database } from './database';
@@ -180,7 +181,7 @@ export class Faker {
datatype: Datatype = new Datatype(this);
- readonly address = new (require('./address'))(this);
+ readonly address: Address = new Address(this);
readonly animal: Animal = new Animal(this);
readonly commerce: Commerce = new Commerce(this);
readonly company = new (require('./company'))(this);