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。