小数点以下8桁の緯度/経度に使用するMySQLデータ型は何ですか?


257

私は地図データを使用してLatitude/Longitudeおり、小数点以下8桁まで拡張しています。例えば:

Latitude 40.71727401
Longitude -74.00898606

私は 使用するGoogleドキュメントで見ました:

lat FLOAT( 10, 6 ) NOT NULL,  
lng FLOAT( 10, 6 ) NOT NULL

ただし、それらの小数点以下の桁数は6
になりますFLOAT(10, 8)。正確にするために、このデータを保存するために別の方法を使用する必要があるかどうかを検討してください。マップ計算で使用されます。ありがとう!


4
地球の表面に1.1mmまで正確な値を保存する必要がありますか?その場合、そもそもなぜlatlngに値を格納しているのですか?
ovangle 2015年


2
グーグルドキュメントは間違っています!floatタイプは使用しないでください。精度は7桁のみです。少なくとも9が必要です。10は必要ありません-奇妙な理由でドキュメントはマイナス記号を数字として数えます。double(9,6)またはのいずれかを実行しますdecimal(9,6)
アリエル

5
どのくらいの精度が本当に必要ですか?小数点以下6桁は、2人がお互いにキスしていることを区別するのに十分な精度を提供します。8は指を区別できます。 FLOAT1.7m(5.6ft)離れた2つのアイテムを区別します。これらのすべては、「マップ」アプリケーションでは途方もなく過剰です。
リックジェームズ

回答:


594

DECIMALは、正確な計算のためのMySQLデータ型です。FLOATとは異なり、その精度は任意のサイズの数値に対して固定されているため、FLOATの代わりに使用すると、一部の計算を行うときに精度エラーを回避できる場合があります。計算せずに数値を格納して取得するだけの場合、実際にはFLOATは安全ですが、DECIMALを使用しても害はありません。計算ではFLOATはほとんど問題ありませんが、8d.pを確実に確認してください。精度はDECIMALを使用する必要があります。

緯度の範囲は-90から+90(度)なので、DECIMAL(10、8)で十分ですが、経度の範囲は-180から+180(度)なので、DECIMAL(11、8)が必要です。最初の数値は保存されている桁数の合計で、2番目の数値は小数点以下の数値です。

要するに: lat DECIMAL(10, 8) NOT NULL, lng DECIMAL(11, 8) NOT NULL

これは、MySQLが浮動小数点データ型を処理する方法を説明します。

更新: MySQLは空間データ型をサポートしPoint、使用可能な単一値型です。例:

CREATE TABLE `buildings` (
  `coordinate` POINT NOT NULL,
  /* Even from v5.7.5 you can define an index for it */
  SPATIAL INDEX `SPATIAL` (`coordinate`)
) ENGINE=InnoDB;

/* then for insertion you can */
INSERT INTO `buildings` 
(`coordinate`) 
VALUES
(POINT(40.71727401 -74.00898606));

11
おそらく、私の回答では、正確な単語を誤って使用した可能性があります。DECIMALは依然として、指定した精度と同じくらい正確なだけです。私のポイントは、それその正確さだということでした。もちろん、いくつかの計算はエラーを拡大します。DECMIAL xを使用している場合、sin(x ^ 100)は大幅に低下します。しかし、(DECIMAL(10、8)またはFLOAT(10、8)を使用して)0.3 / 3を計算した場合、DECIMALは0.100000000000(正しい)、floatは0.100000003974(8dpは正しいが、乗算すると間違っている)を返します。主な違いは、数値の格納方法にあることを理解しています。DECIMALは10進数を格納し、FLOATは2進近似を格納します。
ガンダリッター2012

1
精度の疑いで、私はDOUBLEにします。
Ratata Tata 14年

1
小数点以下8桁は1.1mm(1/16インチ未満)の精度です。なぜ緯度と経度にそれが必要になるのですか?
vartec 2015

1
Facebookは、latに最大12桁、lngに最大13桁を使用するようです。vartecは、小数点以下8桁は1.1mmに等しいと書きました。7と6はどうですか?(私は数学が得意ではありません)。現時点ではdoubleを使用していますが、タイプを変更することで距離の計算ができるかどうかを確認したいと思います。ありがとうございました。
Alain Zelink 2016

4
この質問への回答(gis.stackexchange.com/questions/8650/…)は、緯度と経度の小数点以下の桁数が異なる場合に得られる精度に関する情報を提供します。
ガンダリッター2016年

16

さらに、float値が丸められていることがわかります。

//例:与えられた値41.0473112,29.0077011

float(11,7)| decimal(11,7)
---------------------------
41.0473099 | 41.0473112
29.0077019 | 29.0077011


1
double必要な精度を持つデータ型を使用できます。
アリエル

1
これら2つのポイントを区別できる便利なマップを表示してください。どちらの表現も「不必要に正確」であると私は主張します。
リックジェームズ


7

データ型を符号付き整数として設定できます。座標をSQLに格納する場合、lat * 10000000およびlong * 10000000として設定できます。そして、距離/半径で選択すると、ストレージ座標を10000000に分割します。300K行でテストしたので、クエリの応答時間は良好です。(2 x 2.67GHz CPU、2 GB RAM、MySQL 5.5.49)


どちらが速いですか?これを行うか、浮動小数点または10進数を使用しますか?
Dinidiniz

1
@Dinidiniz-速度の違いは非常に小さいです。行のフェッチは、データベースアクションのタイミングを圧倒します。
リックジェームズ

なぜ10000000なのか?10進数値の後に6桁を超えるとどうなりますか?または、常に小数点以下6桁を返します。
Mahbub Morshed、2018年

@MahbubMorshed-あなたは7桁を意味します-7つのゼロ桁が表示されています。しかし、はい、この手法は常に正確に7桁を保存します。(4バイトの整数を使用する場合、経度の値は180になる可能性があるため、乗数を7桁を超えて増やすことはできず、符号付き整数の最大値のオーバーフローを回避する必要があります。)これは、単精度浮動小数点での格納よりも2桁正確です。経度の値が大きい場合、小数点以下5桁までしかありません。(179.99998および179.99997は同じ浮動小数点値として格納することができる; 179.99996安全遠い179.99998からである)。)
ToolmakerSteve

これは私がどこでも見た中で最高のトレードオフです。ここでは、使用するコードを示し、それがlong / lat値(つまり-180 .. + 180の範囲内)の4バイトの符号付きintで小数点以下7桁を提供することを確認します。小さいサイズ(4B)で優れた精度(〜1cm)。
ToolmakerSteve

6

floatを使用しないでください...座標が丸められ、奇妙な結果が発生します。

小数を使用



4

Lat / LngをMySQLに格納する最良の方法は、SPATIALインデックスを備えたPOINT列(2Dデータ型)を用意することです。

CREATE TABLE `cities` (
  `zip` varchar(8) NOT NULL,
  `country` varchar (2) GENERATED ALWAYS AS (SUBSTRING(`zip`, 1, 2)) STORED,
  `city` varchar(30) NOT NULL,
  `centre` point NOT NULL,
  PRIMARY KEY (`zip`),
  KEY `country` (`country`),
  KEY `city` (`city`),
  SPATIAL KEY `centre` (`centre`)
) ENGINE=InnoDB;


INSERT INTO `cities` (`zip`, `city`, `centre`) VALUES
('CZ-10000', 'Prague', POINT(50.0755381, 14.4378005));

0

Railsでの移行Rubyの使用

class CreateNeighborhoods < ActiveRecord::Migration[5.0]
  def change
    create_table :neighborhoods do |t|
      t.string :name
      t.decimal :latitude, precision: 15, scale: 13
      t.decimal :longitude, precision: 15, scale: 13
      t.references :country, foreign_key: true
      t.references :state, foreign_key: true
      t.references :city, foreign_key: true

      t.timestamps
    end
  end
end

これは経度を-99..99に制限しませんか?これは太平洋の大部分を除外します!
リックジェームズ

これは一例であり、絶対的な真実と見なすべきではありません。別のDECIMAL 10進精度(20、18)などを使用できます。地理データと空間データを保存する必要がある場合は、この目的でpostgisデータベースを使用できます。MySQL Spatial Extensionsは、OpenGISジオメトリモデルに準拠しているため、優れた代替手段です。データベースの移植性を保つ必要があるため、使用しませんでした。postgis.net
gilcierweb

(20,18)+/- 99でもトップに達します。
リックジェームズ

これは一例であり、絶対的な真実と見なすべきではありません。別のDECIMAL 10進精度(20、18)などを使用できます。地理データと空間データを保存する必要がある場合は、この目的でpostgisデータベースを使用できます。MySQL Spatial Extensionsは、OpenGISジオメトリモデルに準拠しているため、優れた代替手段です。データベースの移植性を維持する必要があったため、使用しませんでした。postgis.net
gilcierweb

おい、これは一例であり、小数点があなたの使用は、データベースがちょうど地理的、空間データのために作られたPostGISを支援されていない場合、あなたは、あなたが欲しいの精度を使用することができます
gilcierweb

-1

OğuzhanKURNUÇの回答の精度を使用/証明するためのコード。

概要:
小さいサイズ(4B)で高精度(〜1cm)。

精度は、[-180、180]の範囲の値の7桁の(非常に近い)10進数です。
これは、小数点以下7桁(〜1cm)で、+-180付近で合計9桁(「180」の最初の「1」を数える場合は10桁)になります。
これとは対照的に、合計が7桁しかない4バイトのfloatと比較すると、 + = 180 (〜1m)近くの小数点以下5桁までです。

このアプローチを使用する方法:

const double Fixed7Mult = 10000000;

public static int DecimalDegreesToFixed7(double degrees)
{
    return RoundToInt(degrees * Fixed7Mult);
}

public static double Fixed7ToDecimalDegrees(int fixed7)
{
    return fixed7 / (double)Fixed7Mult;
}

精度のテスト:

/// <summary>
/// This test barely fails in 7th digit to right of decimal point (0.0000001 as delta).
/// Passes with 0.0000002 as delta.
/// </summary>
internal static void TEST2A_LatLongPrecision()
{
    //VERY_SLOW_TEST Test2A_ForRange(-180, 360, 0.0000001);
    //FAILS Test2A_ForRange(-180, 0.1, 0.0000001);

    Test2A_ForRange(-180, 0.1, 0.0000002);
    Test2A_ForRange(0, 0.1, 0.0000002);
    Test2A_ForRange(179.9, 0.1, 0.0000002);
}

/// <summary>
/// Test for the smallest difference.  A: 9.9999994E-08.
/// </summary>
internal static void TEST2B_LatLongPrecision()
{
    double minDelta = double.MaxValue;
    double vAtMinDelta = 0;
    //VERY_SLOW_TEST Test2B_ForRange(-180, 360, ref minDelta, ref vAtMinDelta);
    Test2B_ForRange(-180, 0.1, ref minDelta, ref vAtMinDelta);
    Test2B_ForRange(0, 0.1, ref minDelta, ref vAtMinDelta);
    Test2B_ForRange(179.9, 0.1, ref minDelta, ref vAtMinDelta);

    // Fails. Smallest delta is 9.9999994E-08; due to slight rounding error in 7th decimal digit.
    //if (minDelta < 0.0000001)
    //  throw new InvalidProgramException($"Fixed7 has less than 7 decimal digits near {vAtMinDelta}");

    // Passes.
    if (minDelta < 0.000000099)
        throw new InvalidProgramException($"Fixed7 has less than 7 decimal digits near {vAtMinDelta}");
}

テストで使用されるヘルパーメソッド:

private static void Test2A_ForRange(double minV, double range, double deltaV)
{
    double prevV = 0;
    int prevFixed7 = 0;
    bool firstTime = true;
    double maxV = minV + range;
    for (double v = minV; v <= maxV; v += deltaV) {
        int fixed7 = DecimalDegreesToFixed7(v);
        if (firstTime)
            firstTime = false;
        else {
            // Check for failure to distinguish two values that differ only in 7th decimal digit.
            // Fails.
            if (fixed7 == prevFixed7)
                throw new InvalidProgramException($"Fixed7 doesn't distinguish between {prevV} and {v}");
        }
        prevV = v;
        prevFixed7 = fixed7;
    }
}

private static void Test2B_ForRange(double minV, double range, ref double minDelta, ref double vAtMinDelta)
{
    int minFixed7 = DecimalDegreesToFixed7(minV);
    int maxFixed7 = DecimalDegreesToFixed7(minV + range);

    bool firstTime = true;
    double prevV = 0;   // Initial value is ignored.
    for (int fixed7 = minFixed7; fixed7 < maxFixed7; fixed7++) {
        double v = Fixed7ToDecimalDegrees(fixed7);
        if (firstTime)
            firstTime = false;
        else {
            double delta = Math.Abs(v - prevV);
            if (delta < minDelta) {
                minDelta = delta;
                vAtMinDelta = v;
            }
        }
        prevV = v;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.