http://dev.mysql.com/doc/refman/5.7/en/gis-data-formats.html
http://mysqlserverteam.com/mysql-5-7-and-gis-an-example/
最好的Spatial Database(空間數據庫)當然是Oracle家的,可惜沒用過。最好的開源的Spatial Database一般公認是PostGIS,以前用過一陣子,安裝特別麻煩,不過各種功能很齊全。前段時間嘗試了一下MySQL的spatial extensions,下面記錄了一些使用心得:
1. MySQL Spatial Extensions(后面簡稱MySQL Spatial)功能不夠完全。至少和PostGIS相比是這樣的,它只支持了openGIS(一個標准)的一個子集,包涵有限的幾種空間數據類型(比如Point,LineString,Polygon等),支持的函數也很少,比如,連計算兩個點的distance函數都沒有...
2. MySQL Spatial的安裝配置非常的簡單。其實,它根本不需要安裝。默認的MySQL配置就能夠使用這些空間數據類型。這和PostGIS很不一樣,PostGIS是需要在PostgreSQL上再安裝一個擴展包。
3. 不同的存儲引擎有差別。MyISAM和InnoDB都支持spatial extensions,但差別在於:如果使用MyISAM,可以建立spatial index,而InnoDB是不支持的。這點差別在某些場景下很關鍵,后面會再詳細說說spatial index。
4. POINT的使用。點是最基本也是最常用的一種空間數據類型。MySQL Spatial中用POINT表示點,比如,可以創建一個table:
mysql 5.7
CREATE TABLE address ( address CHAR(80) NOT NULL, address_loc POINT NOT NULL, PRIMARY KEY(address), SPATIAL KEY(address_loc) );
show craate table address CREATE TABLE `address` ( `address` char(80) NOT NULL, `address_loc` point NOT NULL, PRIMARY KEY (`address`), SPATIAL KEY `address_loc` (`address_loc`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8'
其中,address_loc就是一個point類型,說明address_loc是一個點。
插入一個點:
INSERT INTO address VALUES('Foobar street 12', GeomFromText('POINT(2671 2500)')); INSERT INTO address VALUES('Foobar street 13', ST_GEOMFROMTEXT ('POINT(2670 2500)'));
讀取一個點:
select AsText(address_loc) from address;
# AsText(address_loc) 'POINT(2671 2500)' 'POINT(2670 2500)'
分離點X,Y
set @p = GeomFromText('POINT(11 0)'); select x(@p), y(@p);
# x(@p) y(@p) 11 0
The OpenGIS Geometry Model
The Geometry Class Hierarchy
Geometry Class Point Class Curve Class LineString Class Surface Class Polygon Class GeometryCollection Class MultiPoint Class MultiCurve Class MultiLineString Class MultiSurface Class MultiPolygon Class
Supported Spatial Data Formats
Two standard spatial data formats are used to represent geometry objects in queries: Well-Known Text (WKT) format Well-Known Binary (WKB) format
mysql> SELECT ST_X(Point(15, 20)); +---------------------+ | ST_X(Point(15, 20)) | +---------------------+ | 15 | +---------------------+ 1 row in set (0.00 sec)
mysql> SELECT ST_Y(Point(15, 20)); +---------------------+ | ST_Y(Point(15, 20)) | +---------------------+ | 20 | +---------------------+ 1 row in set (0.00 sec)
Fetching Spatial Data
Geometry values stored in a table can be fetched in internal format. You can also convert them to WKT or WKB format. Fetching spatial data in internal format: Fetching geometry values using internal format can be useful in table-to-table transfers: CREATE TABLE geom2 (g GEOMETRY) SELECT g FROM geom; Fetching spatial data in WKT format: The ST_AsText() function converts a geometry from internal format to a WKT string. SELECT ST_AsText(g) FROM geom; Fetching spatial data in WKB format: The ST_AsBinary() function converts a geometry from internal format to a BLOB containing the WKB value. SELECT ST_AsBinary(g) FROM geom;
CREATE TABLE geom (g GEOMETRY); insert into geom select point(1,2); SELECT ST_AsText(g) FROM geom; SELECT ST_AsBinary(g) FROM geom;
Populating Spatial Columns
After you have created spatial columns, you can populate them with spatial data. Values should be stored in internal geometry format, but you can convert them to that format from either Well-Known Text (WKT)
or Well-Known Binary (WKB) format. The following examples demonstrate how to insert geometry values into a table
by converting WKT values to internal geometry format: Perform the conversion directly in the INSERT statement:
INSERT INTO geom VALUES (ST_GeomFromText('POINT(1 1)')); SET @g = 'POINT(1 1)'; INSERT INTO geom VALUES (ST_GeomFromText(@g)); Perform the conversion prior to the INSERT: SET @g = ST_GeomFromText('POINT(1 1)'); INSERT INTO geom VALUES (@g); The following examples insert more complex geometries into the table: SET @g = 'LINESTRING(0 0,1 1,2 2)'; INSERT INTO geom VALUES (ST_GeomFromText(@g)); SET @g = 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'; INSERT INTO geom VALUES (ST_GeomFromText(@g)); SET @g = 'GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'; INSERT INTO geom VALUES (ST_GeomFromText(@g)); The preceding examples use ST_GeomFromText() to create geometry values. You can also use type-specific functions: SET @g = 'POINT(1 1)'; INSERT INTO geom VALUES (ST_PointFromText(@g)); SET @g = 'LINESTRING(0 0,1 1,2 2)'; INSERT INTO geom VALUES (ST_LineStringFromText(@g)); SET @g = 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'; INSERT INTO geom VALUES (ST_PolygonFromText(@g)); SET @g = 'GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'; INSERT INTO geom VALUES (ST_GeomCollFromText(@g)); A client application program that wants to use WKB representations of geometry values is responsible for sending correctly formed WKB
in queries to the server. There are several ways to satisfy this requirement. For example: Inserting a POINT(1 1) value with hex literal syntax: mysql> INSERT INTO geom VALUES -> (ST_GeomFromWKB(0x0101000000000000000000F03F000000000000F03F)); An ODBC application can send a WKB representation, binding it to a placeholder using an argument of BLOB type: INSERT INTO geom VALUES (ST_GeomFromWKB(?)) Other programming interfaces may support a similar placeholder mechanism.
一個比較麻煩的問題是,如何計算兩個POINT的距離?之前說過了,MySQL Spatial不提供distance這個函數。官方指南的做法是這樣的:
GLength(LineStringFromWKB(LineString(point1, point2)))
這條語句大概的意思是用兩個點產生一個LineString的類型,然后調用GLength得到line的長度。
這么做,也對也不對。
對是因為它確實計算的是距離,但是,這種方法計算的是歐式空間的距離。或者簡單的說,它計算的是直線距離。如果兩個點是地理坐標,比如point(116.34, 39.28),想計算地理位置的距離,那么這樣做肯定就不對了。正確的做法應該是使用專門計算地理位置的公式。
5. MySQL Spatial Index的使用。使用這樣的語句:
ALTER TABLE address ADD SPATIAL INDEX(address_loc);
可以在空間數據類型上創建一個spatial index,這個功能只有MyISAM才支持。Index的本質實際上是一個R-TREE,這也是最常用來作為多維數據索引的數據結構。
那么,該如何使用這個index?
舉例來說,假設需要查找某個矩形區域內所有的點,一種方法是這樣:
select * from address where (X(address_loc) > 116.3952) AND (X(address_loc) < 116.4052) AND (Y(address_loc) > 39.8603) AND (Y(address_loc) < 39.8703);
假設我們已經在address_loc這個column上創建了spatial index,所以上述的查詢應該很快。不幸的是,這不是事實。上述的查詢會掃描table內的所有數據,挨個進行計算,建立的index完全不起作用。
正確的做法是,在查詢中使用一些內建的和spatial有關的函數,只有這些函數能夠有效的利用到index。比如,正確的查詢應該是:
select AsText(address_loc) from address where MBRContains(GeomFromText(Polygon((115.3073 40.3821, 115.3173 40.3821, 115.3173 40.4021, 115.3073 40.4021, 115.3073 40.3821))),address_loc);
這里用到了函數MBRContains,用於判斷一個point是否在指定的polygon內部。這個函數就能夠很好的使用之前創建的spatial index。可以做個試驗,比較之前兩個查詢的處理時間,你會發現,后者的速度要快很多。
總的來說,如果只需要做一些簡單的GIS或者LBS的應用,MySQL提供的spatial extensions能夠滿足。但如果需要的功能更復雜一些,MySQL spatial extensions提供的功能可能就不夠用了,需要在MySQL之上自己實現更多的邏輯,或者換成PostGIS。
