Skip to content

Commit

Permalink
* New Address.AreaCircle(lat,lon,rad) to generate random latitude/l…
Browse files Browse the repository at this point in the history
…ongitude GPS points within a specified radius.

* New `Address.Geohash()` generates a random Geohash.
* New `Address.Depth()` generates a random depth (down to Mariana Trench).
* New `Address.Altitude()` generate a random height (up to Mount Everest).
* New `Internet.Color()` format options: CSS `rgb(...)` and delimited RGB.
* New `System.AndroidId()` to generate GCM registration ID.
* New `System.ApplePushToken()` to generate a random Apple Push Token.
* New `System.BlackBerryPin()` to generate a random Black Berry PIN.
* New `Randomizer.Hash()` to generate random hashes of specified length.
* New `Randomizer.String2()` to generate random strings with specified character sets.
  • Loading branch information
bchavez committed Apr 1, 2018
1 parent 641b240 commit 2afd56b
Show file tree
Hide file tree
Showing 14 changed files with 371 additions and 8 deletions.
12 changes: 12 additions & 0 deletions HISTORY.md
@@ -1,3 +1,15 @@
## v22.0.7
* New `Address.AreaCircle(lat,lon,rad)` to generate random latitude/longitude GPS points within a specified radius.
* New `Address.Geohash()` generates a random Geohash.
* New `Address.Depth()` generates a random depth (down to Mariana Trench).
* New `Address.Altitude()` generate a random height (up to Mount Everest).
* New `Internet.Color()` format options: CSS `rgb(...)` and delimited RGB.
* New `System.AndroidId()` to generate GCM registration ID.
* New `System.ApplePushToken()` to generate a random Apple Push Token.
* New `System.BlackBerryPin()` to generate a random Black Berry PIN.
* New `Randomizer.Hash()` to generate random hashes of specified length.
* New `Randomizer.String2()` to generate random strings with specified character sets.

## v22.0.6
* Added `Randomizer.String` method to generate strings. Uses `Chars()` method.
* PR 136: Improve speed of `DataSet.ParseTokens()`. Thanks @danij!
Expand Down
6 changes: 6 additions & 0 deletions LICENSE
Expand Up @@ -21,6 +21,12 @@ https://github.com/zzzprojects/Z.ExtensionMethods/
Copyright (c) 2015 kernys
https://github.com/kernys/Kernys.Bson

Copyright (c) 2015 Victor Quinn
https://github.com/chancejs/chancejs

Copyright (c) 2014 Chris Veness
https://github.com/chrisveness/geodesy/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
Expand Down
68 changes: 66 additions & 2 deletions Source/Bogus.Tests/DataSetTests/AddressTest.cs
@@ -1,13 +1,20 @@
using Bogus.DataSets;
using Bogus.DataSets;
using Bogus.Extensions;
using Bogus.Models;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;
using static System.Math;

namespace Bogus.Tests.DataSetTests
{
public class AddressTest : SeededTest
{
public AddressTest()
private readonly ITestOutputHelper console;

public AddressTest(ITestOutputHelper console)
{
this.console = console;
address = new Address();
}

Expand Down Expand Up @@ -155,5 +162,62 @@ public void can_generate_an_ordnial_direction()
address.OrdinalDirection().Should().Be("Southeast");
address.OrdinalDirection(true).Should().Be("NE");
}

[Fact]
public void can_generate_a_geohash()
{
address.Geohash().Should().Be("m3un1m1");
address.Geohash(5).Should().Be("qg9y5");
}

[Fact]
public void can_generate_depth()
{
address.Depth().Should().BeLessThan(0)
.And.BeGreaterOrEqualTo(-10994);
}

[Fact]
public void can_generate_height()
{
address.Altitude().Should().BeGreaterThan(0)
.And.BeLessOrEqualTo(8848);
}

[Fact]
public void can_get_range()
{
var center = new LatLon // somewhere in the middle of Colorado.
{
Latitude = 39, //north
Longitude = -105, //west
};
var newPoint = address.AreaCircle(center.Latitude, center.Longitude, 1000 * 1000); //radial search around 1000 km.

var distance = GetDistance(center, newPoint);
console.Dump(distance);
distance.Should().BeLessOrEqualTo(1000 * 1000);
}

double GetDistance(LatLon x, LatLon y)
{
// https://github.com/chrisveness/geodesy/blob/master/latlon-spherical.js
// a = sin²(Δφ/2) + cos(φ1)⋅cos(φ2)⋅sin²(Δλ/2)
// tanδ = √(a) / √(1−a)

const int R = 6371000; // Earth's radius in meters
var φ1 = x.Latitude.ToRadians(); var λ1 = x.Longitude.ToRadians();
var φ2 = y.Latitude.ToRadians(); var λ2 = y.Longitude.ToRadians();
var Δφ = φ2 - φ1;
var Δλ = λ2 - λ1;

var a = Sin(Δφ / 2) * Sin(Δφ / 2)
+ Cos(φ1) * Cos(φ2)
* Sin(Δλ / 2) * Sin(Δλ / 2);
var c = 2 * Atan2(Sqrt(a), Sqrt(1 - a));
var d = R * c;

return d;
}
}
}
12 changes: 12 additions & 0 deletions Source/Bogus.Tests/DataSetTests/InternetTests.cs
Expand Up @@ -106,6 +106,18 @@ public void can_get_html_color()
internet.Color().Should().Be("#4d0e68");
}

[Fact]
public void can_get_color_in_format()
{
internet.Color(format:ColorFormat.Rgb).Should().Be("rgb(77,14,104)");
}

[Fact]
public void can_get_color_in_grayscale()
{
internet.Color(grayscale: true).Should().Be("#4d4d4d");
}

[Fact]
public void can_get_url_with_path()
{
Expand Down
18 changes: 18 additions & 0 deletions Source/Bogus.Tests/DataSetTests/SystemTest.cs
Expand Up @@ -133,5 +133,23 @@ public void can_get_file_path_unix()
{
system.FilePath().Should().Be("/sys/bluetooth.js");
}

[Fact]
public void can_get_an_android_id()
{
system.AndroidId().Should().Be("APA91D6QF2E3IvkYaKB52JW1SSkDC5IZpfBzfk6IPaXZfFrXVNTuiA3r6cj6jweAnGGuVMKTEVjTNYPcrpKQeeIRa9s20_qkYoDA-Y1830SoibG9q6IVOqm8-RjLkISEw_XqmfeunBMcolz-wjEWkwyz1vC8GjQoaeTjhhQaUeycF8MGilg13Xk");
}

[Fact]
public void can_get_an_apple_push_token()
{
system.ApplePushToken().Should().Be("91da090b74f2b910be0dd5991af6398351ac2ef3a6eecd74806134147385aa7e");
}

[Fact]
public void can_get_a_black_berry_pin()
{
system.BlackBerryPin().Should().Be("91da090b");
}
}
}
12 changes: 12 additions & 0 deletions Source/Bogus.Tests/RandomizerTest.cs
Expand Up @@ -316,6 +316,18 @@ public void generate_string2_pool_min_max()
x.Should().Be("xzyxyxzy");
}

[Fact]
public void generate_hash()
{
r.Hash().Should().Be("91da090b74f2b910be0dd5991af6398351ac2ef3");
}

[Fact]
public void generate_small_hash()
{
r.Hash(20).Should().Be("91da090b74f2b910be0d");
}

[Fact]
public void random_word_tests()
{
Expand Down
29 changes: 29 additions & 0 deletions Source/Bogus/Chars.cs
@@ -0,0 +1,29 @@
namespace Bogus
{
/// <summary>
/// Static class for holding character string constants.
/// </summary>
public static class Chars
{
/// <summary>
/// Lower case, a-z.
/// </summary>
public const string LowerCase = "abcdefghijklmnopqrstuvwxyz";
/// <summary>
/// Upper case, A-Z.
/// </summary>
public const string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/// <summary>
/// Numbers, 0-9.
/// </summary>
public const string Numbers = "0123456789";
/// <summary>
/// Hexadecimal, 0-9 and a-z.
/// </summary>
public const string HexLowerCase = Numbers + "abcdef";
/// <summary>
/// Hexadecimal, 0-9 and A-Z.
/// </summary>
public const string HexUpperCase = Numbers + "ABCDEF";
}
}
92 changes: 91 additions & 1 deletion Source/Bogus/DataSets/Address.cs
@@ -1,4 +1,7 @@
using System;
using System;
using Bogus.Extensions;
using Bogus.Models;
using static System.Math;

namespace Bogus.DataSets
{
Expand Down Expand Up @@ -220,6 +223,93 @@ public string OrdinalDirection(bool useAbbreviation = false)
return GetRandomArrayItem("direction_abbr", min: 4, max: 8);
return GetRandomArrayItem("direction", min: 4, max: 8);
}

/// <summary>
/// Generates a random Geohash. [See](https://en.wikipedia.org/wiki/Geohash).
/// </summary>
/// <param name="length">By default, includes 7 characters of accuracy.</param>
public string Geohash(int length = 7)
{
return this.Random.String2(length, "0123456789bcdefghjkmnpqrstuvwxyz");
}

/// <summary>
/// Generate a random depth, in meters. Default max depth is -10994m (Mariana Trench). Depths are always negative.
/// </summary>
/// <param name="min">By default, maximum depth is -10994 meters (depth of the Mariana Trench).</param>
public double Depth(double min = -10994)
{
if( min >= 0 ) throw new ArgumentOutOfRangeException(nameof(min), "Depths must be negative.");
return this.Random.Double(min, 0);
}

/// <summary>
/// Generate a random altitude, in meters. Default max height is 8848m (Mount Everest). Heights are always positive.
/// </summary>
/// <param name="max"></param>
public double Altitude(double max = 8848)
{
if( max <= 0 ) throw new ArgumentOutOfRangeException(nameof(max), "Heights must be positive.");
return this.Random.Double(0, max);
}

/// <summary>
/// Get a latitude and longitude within a specific radius in meters.
/// </summary>
/// <param name="centerLat">The center latitude point</param>
/// <param name="centerLon">The center longitude point</param>
/// <param name="radiusMeters">Radial distance from center in meters</param>
public LatLon AreaCircle(double centerLat, double centerLon, double radiusMeters)
{
// https://github.com/chrisveness/geodesy/blob/master/latlon-spherical.js
// https://www.movable-type.co.uk/scripts/latlong.html
//Formula: φ2 = asin(sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ)
// λ2 = λ1 + atan2(sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2)
//
// where φ is latitude
// where λ is longitude
// where θ is the bearing (clockwise from north)
// where δ is the angular distance d/R;
// where d being the distance traveled
// where R the earth’s radius
//
// (all angles in radians)

const double TwoPI = 2 * PI;

const int R = 6371000; // Earth's radius in meters

var φ1 = centerLat.ToRadians();
var λ1 = centerLon.ToRadians();

// Get a distance shorter than radiusMeters.
var d = radiusMeters * this.Random.Double();

// Get a random bearing between [0, 2pi] radians (0-360°)
var brng = this.Random.Double(0, TwoPI);

var φ2 = Asin(Sin(φ1) * Cos(d / R) +
Cos(φ1) * Sin(d / R) * Cos(brng));
var λ2 = λ1 + Atan2(
Sin(brng) * Sin(d / R) * Cos(φ1),
Cos(d / R) - Sin(φ1) * Sin(φ2)
);

var destLat = φ2.ToDegrees();
var destLon = ((λ2.ToDegrees() + 540) % 360) - 180; //and normalize to −180°...+180°

return new LatLon { Latitude = destLat, Longitude = destLon};
}

/// <summary>
/// Get a latitude and longitude within a specific radius in meters.
/// </summary>
/// <param name="center">The center of the circle </param>
/// <param name="radiusMeters">Distance being traveled, in meters</param>
public LatLon AreaCircle(LatLon center, double radiusMeters)
{
return AreaCircle(center.Latitude, center.Longitude, radiusMeters);
}
}

/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions Source/Bogus/DataSets/ColorFormat.cs
@@ -0,0 +1,21 @@
namespace Bogus.DataSets
{
/// <summary>
/// Type of color format
/// </summary>
public enum ColorFormat
{
/// <summary>
/// Hexadecimal format: #4d0e68
/// </summary>
Hex = 0x1,
/// <summary>
/// CSS format: rgb(77,14,104)
/// </summary>
Rgb,
/// <summary>
/// Delimited RGB: 77,14,104
/// </summary>
Delimited
}
}
29 changes: 25 additions & 4 deletions Source/Bogus/DataSets/Internet.cs
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Bogus.Vendor;

Expand Down Expand Up @@ -213,13 +211,36 @@ public string Password(int length = 10, bool memorable = false, string regexPatt
/// <param name="baseRed">Red base color</param>
/// <param name="baseGreen">Green base color</param>
/// <param name="baseBlue">Blue base color</param>
public string Color(byte baseRed = 0, byte baseGreen = 0, byte baseBlue = 0)
/// <param name="grayscale">Output a gray scale color</param>
/// <param name="format">The color format</param>
public string Color(byte baseRed = 0, byte baseGreen = 0, byte baseBlue = 0, bool grayscale = false, ColorFormat format = ColorFormat.Hex)
{
var red = Math.Floor((Random.Number(256) + (double)baseRed) / 2);
var green = Math.Floor((Random.Number(256) + (double)baseGreen) / 2);
var blue = Math.Floor((Random.Number(256) + (double)baseBlue) / 2);

return string.Format("#{0:x02}{1:x02}{2:x02}", (byte)red, (byte)green, (byte)blue);
if( grayscale )
{
green = red;
blue = red;
}

if( format == ColorFormat.Hex )
{
return string.Format("#{0:x02}{1:x02}{2:x02}", (byte)red, (byte)green, (byte)blue);
}

if( format == ColorFormat.Delimited )
{
return DelimitedRgb();
}

return $"rgb({DelimitedRgb()})";

string DelimitedRgb()
{
return $"{(byte)red},{(byte)green},{(byte)blue}";
}
}

/// <summary>
Expand Down

0 comments on commit 2afd56b

Please sign in to comment.