हम double
. का उपयोग कर रहे हैं latitude
स्टोर करने के लिए और longitude
. इसके अलावा हम उन सभी मानों को (ट्रिगर द्वारा) पूर्वगणना करते हैं जो केवल एक बिंदु को देखते समय पूर्व-गणना योग्य होते हैं। मेरे पास वर्तमान में उस सूत्र तक पहुंच नहीं है जिसका हम उपयोग कर रहे हैं, इसे बाद में जोड़ेंगे। यह इष्टतम गति/सटीक संतुलन के लिए अनुकूलित है।
परिभाषित क्षेत्र खोजों के लिए (मुझे x किमी के भीतर सभी बिंदु दें) हम अतिरिक्त रूप से lat/lng मान को 1e6
से गुणा करके संग्रहीत करते हैं (1,000,000) इसलिए हम पूर्णांक श्रेणियों की तुलना करके एक वर्ग में सीमित कर सकते हैं जो बिजली की तेजी से उदा.
lat BETWEEN 1290000 AND 2344000
AND
lng BETWEEN 4900000 AND 4910000
AND
distformularesult < 20
संपादित करें:
PHP में वर्तमान स्थान के मानों का सूत्र और पूर्व-परिकलन यहां दिया गया है।
WindowSize वह मान है जिसके साथ आपको खेलना है, यह डिग्री फ़ैक्टर 1e6 है, जिसका उपयोग केंद्र के चारों ओर एक वर्ग में संभावित परिणामों को कम करने के लिए किया जाता है, परिणाम खोजने की गति तेज करता है - यह न भूलें कि यह कम से कम होना चाहिए आपके खोज के दायरे का आकार.
$paramGeoLon = 35.0000 //my center longitude
$paramGeoLat = 12.0000 //my center latitude
$windowSize = 35000;
$geoLatSinRad = sin( deg2rad( $paramGeoLat ) );
$geoLatCosRad = cos( deg2rad( $paramGeoLat ) );
$geoLonRad = deg2rad( $paramGeoLon );
$minGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) - $windowSize;
$maxGeoLatInt = intval( round( ( $paramGeoLat * 1e6 ), 0 ) ) + $windowSize;
$minGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) - $windowSize;
$maxGeoLonInt = intval( round( ( $paramGeoLon * 1e6 ), 0 ) ) + $windowSize;
मेरे केंद्र की एक विशिष्ट श्रेणी के भीतर सभी पंक्तियों को खोजना
SELECT
`e`.`id`
, :earthRadius * ACOS ( :paramGeoLatSinRad * `e`.`geoLatSinRad` + :paramGeoLatCosRad * `m`.`geoLatCosRad` * COS( `e`.`geoLonRad` - :paramGeoLonRad ) ) AS `geoDist`
FROM
`example` `e`
WHERE
`e`.`geoLatInt` BETWEEN :paramMinGeoLatInt AND :paramMaxGeoLatInt
AND
`e`.`geoLonInt` BETWEEN :paramMinGeoLonInt AND :paramMaxGeoLonInt
HAVING `geoDist` < 20
ORDER BY
`geoDist`
फॉर्मूलर में काफी अच्छी सटीकता होती है (एक मीटर से नीचे, यह निर्भर करता है कि आप कहां हैं और बिंदु के बीच कितनी दूरी है)
मैंने अपनी डेटाबेस तालिका example
. में निम्न मानों का पूर्व-परिकलन किया है
CREATE TABLE `example` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`geoLat` double NOT NULL DEFAULT '0',
`geoLon` double NOT NULL DEFAULT '0',
# below is precalculated with a trigger
`geoLatInt` int(11) NOT NULL DEFAULT '0',
`geoLonInt` int(11) NOT NULL DEFAULT '0',
`geoLatSinRad` double NOT NULL DEFAULT '0',
`geoLatCosRad` double NOT NULL DEFAULT '0',
`geoLonRad` double NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `example_cIdx_geo` (`geoLatInt`,`geoLonInt`,`geoLatSinRad`,`geoLatCosRad`,`geoLonRad`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC
उदाहरण ट्रिगर
DELIMITER $
CREATE TRIGGER 'example_before_insert' BEFORE INSERT ON `example` FOR EACH ROW
BEGIN
SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
END$
CREATE TRIGGER 'example_before_update' BEFORE UPDATE ON `example` FOR EACH ROW
BEGIN
IF NEW.geoLat <> OLD.geoLat OR NEW.geoLon <> OLD.geoLon
THEN
SET NEW.`geoLatInt` := CAST( ROUND( NEW.`geoLat` * 1e6, 0 ) AS SIGNED INTEGER );
SET NEW.`geoLonInt` := CAST( ROUND( NEW.`geoLon` * 1e6, 0 ) AS SIGNED INTEGER );
SET NEW.`geoLatSinRad` := SIN( RADIANS( NEW.`geoLat` ) );
SET NEW.`geoLatCosRad` := COS( RADIANS( NEW.`geoLat` ) );
SET NEW.`geoLonRad` := RADIANS( NEW.`geoLon` );
END IF;
END$
DELIMITER ;
प्रशन? नहीं तो मजे करो :)