In the SQL Server 2012 there is methods to validate geography '.IsValidDetailed()' and to change orientation '.ReorientObject (geography)'.
I am working with SQL server 2008 and facing problems of polygon orientation.
Question is -
is there any solution to validate geography polygon or change the orientation to valid geography?
is it possible to copy that IsValidDetailed method from sqlserver 2012 to 2008?
if I have to do it manually, then what is the correct orientation of polygon for valid geography datatype?
We can validate geometry datatype in SQL server 2008, is it ok to convert geography polygon in to geometry polygon and validated it?
Please assist. Thanks in advance
This is working for me on SQL Server 2008. After loading the shape as a geometry, use MakeValid() to correct it, then reload into a geography.
declare #gt nvarchar(max)
declare #gm geometry
declare #gmvalid geometry
set #gmvalid = #gm.MakeValid()
set #gt = #gmvalid.STAsText()
--select #gt
if LEFT(#gt,7 ) = 'POLYGON'
begin
set #gg = geography::STPolyFromText(#gt, 4326)
end
else
begin
set #gg = geography::STMPolyFromText(#gt, 4326)
end
I found solution, SQL Server Saptial Tools
http://sqlspatialtools.codeplex.com/
Followings are the methods solved my problem.
IsValidGeographyFromText(string inputWKT, int srid)
Check if an input WKT can represent a valid geography. This function requires that
the WTK coordinate values are longitude/latitude values, in that order and that a valid
geography SRID value is supplied. This function will not throw an exception even in
edge conditions (i.e. longitude/latitude coordinates are reversed to latitude/longitude).
SqlGeography MakeValidGeographyFromText(string inputWKT, int srid)
Convert an input WKT to a valid geography instance.
This function requires that the WKT coordinate values are longitude/latitude values,
in that order and that a valid geography SRID value is supplied.
Related
I have a data dump (csv) of place names from https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/products/os-open-names.html
I need to import this into mysql however the geometry co-ordinates use BNG (OSGB36). Does Mysql have any functions to convert these co-ordinates to wgs84 lat/long or is there any other sql method to achieve this?
another option is perhaps loading it into postgis - does postgis have any funtion to transform BNG to lat/long? Perhaps I could do that and then export the data once transformed and load it into mysql?
In postgis I could do something as follows (not tested)
select AddGeometryColumn('locations', 'the_geom', 27700, 'POINT', 2);
-- X and Y are the BNG co-ordinates
UPDATE locations SET the_geom = ST_GeomFromText('POINT(' || x || ' ' || y || ')', 27700 );
alter table locations add column lat real;
alter table locations add column long real;
update locations set long=st_x(st_transform(the_geom,4326)),
lat=st_y(st_transform(the_geom,4326));
Is it possible to do these type of function in mysql - basically what are the equivalent functions in mysql. I cant seem to figure the syntax out? The following doesnt work in mysql:
update locations set long=ST_X(ST_Transform(the_geom,4326)),
lat=ST_Y(ST_Transform(the_geom,4326));
I get error function ST_Transform does not exist. I'm using mysql 5.7
* UPDATE *
Sample data:
NAME1 LOCAL_TYPE GEOMETRY_X GEOMETRY_Y DISTRICT_BOROUGH REGION COUNTRY
Southport Town 333510 417225 Sefton North West England
The answer is, of all places, here
Answered by Hartmut Holzgraefe in this comment.
So far the SRID property is just a dummy in MySQL, it is stored as
part of a geometries meta data but all actual calculations ignore it
and calculations are done assuming Euclidean (planar) geometry.
So ST_Transform would not really do anything at this point anyway.
I think the same is still true for MariaDB, at least the knowledge [sic]
base page for the SRID() function still says so:
This WorkLog discusses the progress of implementing ST_Transform.
MySQL 8.0 does seem to have it implemented: https://dev.mysql.com/doc/refman/8.0/en/spatial-operator-functions.html#function_st-transform
So, the solution may require upgrading to MySQL 8.0.
The changelog for the very recent 8.0.13 says:
----- 2018-10-22 8.0.13 General Availability -- -- -----
MySQL now implements the
ST_Transform()
spatial function for use in converting geometry values from one
spatial reference system (SRS) to another. Currently, it supports
conversion between geographic SRSs. For details, see Spatial Operator
Functions.
I'm trying to create a web map which contains large polygon data-sets. In order to increase performance, I'm hoping to decrease polygon detail when zooming out.
Is MySQL able to simplify polygons and other geometries as part of a query?
EDIT: As James has pointed out, since 5.7, MySQL does support ST_Simplify.
I am afraid there is no simplify function in MySQL spatial. I filed that as a feature request five years ago, and you can see it has received no attention since then.
You have a number of options depending on whether you want to do this as a one off, or if you have dynamic data.
1). Write a quick and dirty function using the PointN function to access your points, only taking every 5th point say, creating a WKT string representing a simplified geometry, and then recreating a geometry using the GeomFromText function.
2). Dump your polygon as WKT, using AsText(geom) out to csv. Import into Postgres/Postgis using COPY command (equivalent of LOAD DATA INFILE), and use the ST_Simplify function there, then reverse the process to bring in back into MySQL.
3). Use ogr2ogr to dump to shp format, and then a tool like mapshaper to simplify it, output to shp and then import again using ogr2ogr. Mapshaper is nice, because you can get a feel for how the algorithm works, and could potentially use it to implement your own, instead of option 1.
There are other options, such as using Java Topology Suite if you are using Java server side, but I hope this gives you some idea of how to proceed.
I am sorry that the initial answer is, No. I went through this a number of years ago and eventually made a permanent switch to Postgres/Postgis, as it is much more fully featured for spatial work.
MySQL 5.7 contains ST_Simplify function for simplifying geometries.
From https://dev.mysql.com/doc/refman/5.7/en/spatial-convenience-functions.html:
ST_Simplify(g, max_distance)
Simplifies a geometry using the Douglas-Peucker algorithm and returns a simplified value of the same type, or NULL if any argument is NULL.
MySQL 5.7 includes a huge overhaul of spatial functionality compared to 5.6 and is now in General Availability status as of 5.7.9.
I had a use case where I wanted to use ST_Simplify, but the code had to run on MySQL 5.6 (which doesn't have it). Therefore, I developed a solution like the one suggested by John Powell in another answer.
MySQL does not unfortunately offer any aggregates, whereby you can create geometry by progressively adding points to it (i.e. there is no ST_AddPoint or similar). The only way you can compose a geometry is by building it step-by-step as a WKT string, then finally converting the completed string to a geometry.
Here is an example of a stored function that accepts a MultiLineString, and simplifies each LineString in it by only keeping every nth point, making sure that the start and end points are always kept. This is done by looping through the LineStrings in the MultiLineString, then through the points for each (skipping as required), and accumulating the lot in a WKT string, which is finally converted to a geometry using ST_GeomCollFromText.
-- geometryCollection: MultiLineString collection to simplify
-- skip: Number of points to remove between every two points
CREATE FUNCTION `sp_CustomSimplify`(gc geometrycollection, skip INT) RETURNS geometrycollection
BEGIN
DECLARE i, j,numLineStrings, numPoints INT;
DECLARE ls LineString;
DECLARE pt, lastPt Point;
DECLARE ls LineString;
DECLARE lastPt Point;
DECLARE txt VARCHAR(20000);
DECLARE digits INT;
SET digits = 4;
-- Start WKT string:
SET txt = 'MULTILINESTRING(';
-- Loop through the LineStrings in the geometry (which is a MultiLineString)
SET i = 1;
SET numLineStrings = ST_NumGeometries(gc);
loopLineStrings: LOOP
IF i > numLineStrings THEN LEAVE loopLineStrings; END IF;
SET ls = ST_GeometryN(gc, i);
-- Add first point to LineString:
SET pt = ST_StartPoint(ls);
SET txt = CONCAT(txt, '(', TRUNCATE(ST_X(pt),digits), ' ', TRUNCATE(ST_Y(pt),digits));
-- For each LineString, loop through points, skipping
-- points as we go, adding them to a running text string:
SET numPoints = ST_NumPoints(ls);
SET j = skip;
loopPoints: LOOP
IF j > numPoints THEN LEAVE loopPoints; END IF;
SET pt = ST_PointN(ls, j);
-- For each point, add it to a text string:
SET txt = CONCAT(txt, ',', TRUNCATE(ST_X(pt),digits), ' ', TRUNCATE(ST_Y(pt),digits));
SET j = j + skip;
END LOOP loopPoints;
-- Add last point to LineString:
SET lastPt = ST_EndPoint(ls);
SET txt = CONCAT(txt, ',', TRUNCATE(ST_X(lastPt),digits), ' ', TRUNCATE(ST_Y(lastPt),digits));
-- Close LineString WKT:
SET txt = CONCAT(txt, ')');
IF(i < numLineStrings) THEN
SET txt = CONCAT(txt, ',');
END IF;
SET i = i + 1;
END LOOP loopLineStrings;
-- Close MultiLineString WKT:
SET txt = CONCAT(txt, ')');
RETURN ST_GeomCollFromText(txt);
END
(This could be a lot prettier by extracting bits into separate functions.)
Issue:
When executing #somegeog.STWithin(#othergeog) I get the execption:
Msg 6506, Level 16, State 10, Line 5
Could not find method 'STWithin' for type 'Microsoft.SqlServer.Types.SqlGeography' in assembly 'Microsoft.SqlServer.Types'
Background:
I am working with SQL Server (2008 R2) Geography data for the first time. I have a list of points in a table, and I want to retrieve those in a given area. I intend to write a stored procedure that accepts latitude and longitude bounds for the area.
I can write this procedure easily by extracting the latitude and longitude from the stored geography points, but I was hoping to let the inbuilt functionality do the work. After googling the topic for a while, I found the STWithin method, but even using Microsoft's example, I get an error claiming the method doesn't exist.
MS Example from link:
DECLARE #g geography;
DECLARE #h geography;
SET #g = geography::Parse('POLYGON ((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566))');
SET #h = geography::Parse('CURVEPOLYGON (COMPOUNDCURVE (CIRCULARSTRING (-122.200928 47.454094, -122.810669 47.00648, -122.942505 46.687131, -121.14624 45.786679, -119.119263 46.183634), (-119.119263 46.183634, -119.273071 47.107523), CIRCULARSTRING (-119.273071 47.107523, -120.640869 47.569114, -122.200928 47.454094)))');
SELECT #g.STWithin(#h);
Can somebody please explain what I'm doing wrong?
Thanks in Advance!
I think the example you are looking at applies to SQL Server 2012. Try the example here: http://technet.microsoft.com/en-us/library/bb933991.aspx
You can use STIntersects() with a geography data type in SQL Server 2008. This is much faster than using .Lat and .Long if you have your spatial index set up.
I'm working on a project where I have places with latitude and longitude coordinates stored in my database. I use Google Maps to plot these places as markers on a map. I don't want to plot any "invisible" markers (markers outside the current viewport/bounding box) to the map. Therefore I follow Googles advice regarding viewport marker management.
I have a working solution where i use AJAX to query an ASP.NET web service whenever the viewport of my map has changed. This web service calls a stored procedure in my MSSQL database to get all places that have coordinates that are inside the current viewport/bounding box. The stored procedure looks like this:
ALTER PROCEDURE dbo.GetPlacesInsideBoundingBox
(
#swLat VarChar(11), /* south-west point latitude */
#swLng VarChar(11), /* south-west point longitude */
#neLat VarChar(11), /* north-east point latitude */
#neLng VarChar(11) /* north-east point longitude */
)
AS
/* SET NOCOUNT ON */
SELECT * FROM dbo.Place WHERE
place_lat >= CONVERT(Decimal(11,8), #swLat)
AND place_lat <= CONVERT(Decimal(11,8), #neLat)
AND place_lng >= CONVERT(Decimal(11,8), #swLng)
AND place_lng <= CONVERT(Decimal(11,8), #neLng)
RETURN
The parameters that are sent to the stored procedure are simply the latitude and longitude of the south-west and the north-east corners of the viewport/bounding box. Then I compare these points with the longitude and latitude values stored in my database to see which places are inside the current viewport/bounding box.
This works fine! But I read that you can run into problems if you have a negative value for the longitude (west of the Prime Meridian) and that the simple solution was to add 360 to the longitude if it was negative.
I have two questions:
How do I alter my stored procedure (above) to take into account negative
longitudes?
Are there any other modifications I should consider making to this
stored procedure to make it foolproof?
If you are wondering about the conversion from VarChar to Decimal, I found it much easier to work with simple strings on the client side (javascript) and then convert them to decimal numbers in my stored procedure when I needed to do the calculations.
Thanks in advance!
1) Longitude normaly should be between -180 to 180 so just check if the coordinates from google fulfil this condition. Probably yes. If not, just normalize them to be within this interval by adding or subtracting 360. Normalize them before you call the stored procedure.
2) Normally #swLng <= #neLng, but you must also handle the case #swLng > #neLng. So inside your stored procedure, your longitude condition would look like:
AND
(
(CONVERT(Decimal(11,8), #swLng) <= CONVERT(Decimal(11,8), #neLng)
AND place_lng >= CONVERT(Decimal(11,8), #swLng)
AND place_lng <= CONVERT(Decimal(11,8), #neLng)
)
OR
(CONVERT(Decimal(11,8), #swLng) > CONVERT(Decimal(11,8), #neLng)
AND (place_lng >= CONVERT(Decimal(11,8), #swLng)
OR place_lng <= CONVERT(Decimal(11,8), #neLng))
)
)
P.S.: Note that you should probably use GIS like PostGIS for this to be really efficient :) I was about to solve this problem but I still postponed it "until I have a GIS". You inspired me and maybe I'll go for this solution too.
P.S.: I'm not sure how your database engine optimizes but I guess for increased efficiency it might help if you define latitude and longitude as indexes - so that it can solve the condition faster. But I'm not sure at all.
Actually I found a better solution. SQL Server has built-in data types for working with spatial data. So I added a new value to my table and called it place_geo with the data type geography. Then I populated it with geography points (created from the latitudes and longitudes I already had in the database). When I had the points stored as geography points in my database, the stored procedure becomes much simpler in its syntax and I also think it will execute much faster.
Here is my stored procedure:
ALTER PROCEDURE dbo.GetPlaceClosestToMe
(
#my_lat Decimal(11,8),
#my_lng Decimal(11,8)
)
AS
DECLARE #my_point geography;
SET #my_point = geography::Point(#my_lat, #my_lng , 4326); /* SRID 4326 */
SET NOCOUNT ON
SELECT TOP 1
place_id,
place_name,
#my_point.STDistance(place_geo)/1000 [Distance in km]
FROM Places
ORDER BY #my_point.STDistance(place_geo)
So what does it do?
First I get a longitude and a latitude as input parameters in decimal format. I declare a new variable (#my_point) that has the data type "geography". I then assign a new geography point to the variable I just created using the longitude and latitude input parameters.
Then I ask for the point in my database table (place_geo) that is closest to #my_point. (STDistance returns the shortest line between two geography types.) The division by 1000 is to get a distance expressed in kilometers.
If you want it to return several results, just alter the select: SELECT TOP 5 or SELECT TOP 10.
At the moment I don't have enough test data to test if this is faster than using the old method, but perhaps someone else have done some tests? I would guess that using the geography data type is much faster than the old method.
With my limited test data, the old stored procedure and this new stored procedure return the same results.
Hope you found this follow-up useful!
Is it possible to change the srid of a column of geometry type? I just want to create a view of geometry type data from the raw latlon data and use it in the geoserver. However after using the pointfromtext function, the type of data I generate is geometry rather than point and geoserver would treat it as an feature typ of byte array which can not be used in the geoserver. However if I use the 'point' function directly in the mysql, I can get the exact type of point however the srid is not right.
So my question is can I set the srid for the geometry type of data?
This is one way of doing it in MySQL:
UPDATE table SET shape = ST_GeomFromText(ST_AsText(shape), SRID);
Where SRID should be the new SRID code (for example 4326 for WGS84). Keep in mind that this only changed the SRID code and not the actual coordinates stored in the shape.
Actually to do what you want in SQL Server 2008, I had to do the following (change all the data in EPGS:4326):
update TestGeom set geom = geometry::STGeomFromText(geom.STAsText(), 4326)
I don't know if in MySQL you can do the same kind of thing. Otherwise, you can rebuild your table with something similar to this:
update TestGeom
set geom = geometry::STGeomFromText('POINT ('+ REPLACE(CONVERT(nvarchar, TestGeom.Lon), ',','.')+' '+REPLACE(CONVERT(nvarchar, TestGeom.Lat), ',','.')+' )', 4326)
I hope it can help you.
I was able to do this in MySQL 5.7 using the following technique:
update location_polygons
set multipoly = ST_GeomFromGeoJSON(ST_AsGeoJSON(multipoly), 2, 0)
where SRID(multipoly) <> 0
Based on this documentation URL: https://dev.mysql.com/doc/refman/5.7/en/spatial-geojson-functions.html