I would like to convert from EPSG:4326 to UTM (30N/EPSG:32630 or 29N/EPSG:32629) in PostGIS. I do the following query but I get wrong results:
SELECT ST_AsText(ST_Transform(ST_GeomFromText('POINT(36.5277099609375 -5.86424016952515)',4326),32630)) As check;
I get "POINT(5262418.33128724 -839958.963432011)" when it should be something approximate to 243625.00,4046330.00 in UTM 30N. If I do the conversion from 4326 to UTM I get the right result but not from UTM to 4326.
What's wrong with the query?
And are there anyway to get the UTM timezone from the coordinates in
EPSG:4326 because I don't know if they belong to 30N or 29N?
1) Your query is correct but you coordinates are inverted. The correct coordinates order in the WKT format is POINT(x y), also POINT(longitude latitude)
This query give you the expected result:
SELECT ST_AsText(ST_Transform(ST_GeomFromText('POINT(-5.86424016952515 36.5277099609375)',4326),32630)) As check;
2) To get the UTM zone from a lat/long geometry you can use this formula:
ST_X(input_geometry)+180)/6)+1
with some adjustments.
For this purpose we use this function:
CREATE OR REPLACE FUNCTION get_utmzone(input_geom geometry)
RETURNS integer AS
$BODY$
DECLARE
zone int;
pref int;
BEGIN
IF GeometryType(input_geom) != 'POINT' THEN
RAISE EXCEPTION 'Input geom must be a point. Currently is: %', GeometryType(input_geom);
END IF;
IF ST_Y(input_geom) >0 THEN
pref:=32600;
ELSE
pref:=32700;
END IF;
zone = floor((ST_X(input_geom)+180)/6)+1;
RETURN zone+pref;
END;
$BODY$
LANGUAGE plpgsql IMMUTABLE;
Use it with this query:
SELECT get_utmzone(ST_GeomFromText('POINT( -5.86424016952515 36.5277099609375)',4326));
The result should be 32630
Firs thing is that following documentation of OpenGIS WKT Point(x,y) yours POINT(36.5277099609375 -5.86424016952515) is south of equator so you have to use 29S(EPSG:32729) and 30S(EPSG:32730)
The reason is because is not POINT(36.5277099609375 -5.86424016952515), but POINT(-5.86424016952515 36.5277099609375)because longitude and latitude depends on the system. Normally X=longitude and Y=latitude, but e.g. in Google Maps X is the lat and Y is the long.
Related
I have a work order in Maximo 7.6.1.1:
The WO has LatitudeY and LongitudeX coordinates in the Service Address tab.
The WO has a custom zone field.
And there is a feature class (polygons) in a separate GIS database.
I want to do spatial query to return an attribute from the polygon record that the WO intersects and use it to populate zone in the WO.
How can I do this?
Related keyword: Maximo Spatial
To do this live in Maximo using an automation script is possible or by writing custom code into Spatial (more challenging). You want to use the /MapServer/identify tool and post the geometry xy, coordinate system, and the layer you want to query. identify window
You will have to format the geometry object correctly and test your post from the window. I usually grab the post from the network section of developer tools once I get it to work and change the output format to json and use it in my code.
You may actually not need to touch your Maximo environment at all. How about just using a trigger on your work orders table ? That trigger can then automatically fill the zone ID from a simple select statement that matches x and y with the zones in the zones table. Here is how that could look like.
This assumes that your work orders are in a table like this:
create table work_orders (
wo_id number primary key,
x number,
y number,
zone_id number
);
and the zones in a table like this
create table zones (
zone_id number primary key,
shape st_geometry
)
Then the trigger would be like this
create or replace trigger work_orders_fill_zone
before insert or update of x,y on work_orders
for each row
begin
select zone_id
into :new.zone_id
from zones
where sde.st_contains (zone_shape, sde.st_point (:new.x, :new.y, 4326) ) = 1;
end;
/
Some assumptions:
The x and y columns contain coordinates in WGS84 longitude/latitude (not in some projection or some other long/lat coordinate system)
Zones don't overlap: a work order point is always therefore in one and only one zone. If not, then the query may return multiple results, which you then need to handle.
Zones fully cover the territory your work orders can take place in. If a work order location can be outside all your zones, then you also need to handle that (the query would return no result).
The x and y columns are always filled. If they are optional, then you also need to handle that case (set zone_id to NULL if either x or y is NULL)
After that, each time a new work order is inserted in the work_orders table, the zone_id column will be automatically updated.
You can initialize zone_id in your existing work orders with a simple update:
update work_orders set x=x, y=y;
This will make the trigger run for each row in the table ... It may take some time to complete if the table is large.
Adapt the code in the Library Scripts section of Maximo 76 Scripting Features (pdf):
#What the script does:
# 1. Takes the X&Y coordinates of a work order in Maximo
# 2. Generates a URL from the coordinates
# 3. Executes the URL via a separate script/library (LIB_HTTPCLIENT)
# 4. Performs a spatial query in an ESRI REST feature service (a separate GIS system)
# 5. Returns JSON text to Maximo with the attributes of the zone that the work
# order intersected
# 6. Parses the zone number from the JSON text
# 7. Inserts the zone number into the work order record
from psdi.mbo import MboConstants
from java.util import HashMap
from com.ibm.json.java import JSONObject
field_to_update = "ZONE"
gis_field_name = "ROADS_ZONE"
def get_coords():
"""
Get the y and x coordinates(UTM projection) from the WOSERVICEADDRESS table
via the SERVICEADDRESS system relationship.
The datatype of the LatitdeY and LongitudeX fields is decimal.
"""
laty = mbo.getDouble("SERVICEADDRESS.LatitudeY")
longx = mbo.getDouble("SERVICEADDRESS.LongitudeX")
#Test values
#laty = 4444444.7001941890
#longx = 666666.0312127020
return laty, longx
def is_latlong_valid(laty, longx):
#Verify if the numbers are legitimate UTM coordinates
return (4000000 <= laty <= 5000000 and
600000 <= longx <= 700000)
def make_url(laty, longx, gis_field_name):
"""
Assembles the URL (including the longx and the laty).
Note: The coordinates are flipped in the url.
"""
url = (
"http://hostname.port"
"/arcgis/rest/services/Example"
"/Zones/MapServer/15/query?"
"geometry={0}%2C{1}&"
"geometryType=esriGeometryPoint&"
"spatialRel=esriSpatialRelIntersects&"
"outFields={2}&"
"returnGeometry=false&"
"f=pjson"
).format(longx, laty, gis_field_name)
return url
def fetch_zone(url):
# Get the JSON text from the feature service (the JSON text contains the zone value).
ctx = HashMap()
ctx.put("url", url)
service.invokeScript("LIBHTTPCLIENT", ctx)
json_text = str(ctx.get("response"))
# Parse the zone value from the JSON text
obj = JSONObject.parse(json_text)
parsed_val = obj.get("features")[0].get("attributes").get(gis_field_name)
return parsed_val
try:
laty, longx = get_coords()
if not is_latlong_valid(laty, longx):
service.log('Invalid coordinates')
else:
url = make_url(laty, longx, gis_field_name)
zone = fetch_zone(url)
#Insert the zone value into the zone field in the work order
mbo.setValue(field_to_update, zone, MboConstants.NOACCESSCHECK)
service.log(zone)
except:
#If the script fails, then set the field value to null.
mbo.setValue(field_to_update, None, MboConstants.NOACCESSCHECK)
service.log("An exception occurred")
LIBHTTPCLIENT: (a reusable Jython library script)
from psdi.iface.router import HTTPHandler
from java.util import HashMap
from java.lang import String
handler = HTTPHandler()
map = HashMap()
map.put("URL", url)
map.put("HTTPMETHOD", "GET")
responseBytes = handler.invoke(map, None)
response = String(responseBytes, "utf-8")
i have one question with regard to MYSQL. I want to create a function that is able to check whether an Input is given in a specific format.
The output should be in the following shape:
***x x (a) n (n) (n) (n)
with :
x = letters and numbers
n = numbers
a = letters
brackets = optional Values***
So my Solution until now is this (User 'Nick' helped me):
CREATE FUNCTION validate_number(testnumber VARCHAR(7))
RETURNS INT
DETERMINISTIC
RETURN testnumber REGEXP '^[[:alnum:]]{2}[[:alpha:]]?[[:digit:]]{1,4}$';
And this approach works for most cases.
But when i enter a value that exceeds the possible amount of elements (max elements = 7) i get no result.
example:
validate_number('00A00002345')
=> no result.
Do you guys have an idea what the problem is?
Thank you very much in advance.
you are actually pointing out the solution of the problem :)
just change VARCHAR(7) to something bigger VARCHAR(2000)
When I run your function, I get the error:
select validate_number('00A00002345')
Data too long for column 'testnumber' at row 1
You can add a length to the varchar.
CREATE FUNCTION validate_number (
in_testnumber VARCHAR(32000)
)
Or, use text:
CREATE FUNCTION validate_number (
in_testnumber text
)
RETURNS INT
DETERMINISTIC
BEGIN
RETURN (in_testnumber REGEXP '^[[:alnum:]]{2}[[:alpha:]]?[[:digit:]]{1,4}$');
END;
Could someone please help me with this one?
So I need to write a user input function in which I need to concatenate two strings. When outputted, there must be a space between the two strings, note there is not a space in the two strings when inputting them. Test functions with the following, String 1: Spring, String 2: Break!
This is my solution:
create function concatenate(X CHAR,Y CHAR)
Returns CHAR(50)
Return concat(X, ' ', Y);
select concatenate('Spring','Break')
However, the problem is that sql only returns the first letter of each word, which is "S B". But I want it to be "Spring Break"
Any ideas on this one? Helps are very appreciated
Supply a length for the input parameters as well:
create function concatenate(X CHAR(24),Y CHAR(24))
Returns CHAR(50)
Return concat(X, ' ', Y);
select concatenate('Spring','Break')
You need to define the size when you declare the argument.
create function con(X char(50), Y char(50))
returns char(100)
You have to specify the size of CHAR(), otherwise it will use the default CHAR(1), and you can't get want you want.
eg:
create function hello(x char(10),y char(10))
returns char(30) deterministic
return concat(x,' ',y)`
select hello('Hello','World');
Hello World
I am loading a xml file into my postgresql / postgis database the following field contains cords for lat long of the area im wanting to convert to a polygon. Is there a way to use ST_MPolyFromText to make this work the samples have the long and lat reversed from the order than the way its loading it into the db?
39.43,-80.29 39.46,-80.49 39.43,-80.52 39.46,-80.66 39.98,-80.76 40.07,-80.43 39.46,-79.91 39.39,-80.10 39.40,-80.11 39.39,-80.18 39.43,-80.29
You can format this as WKT quite easily. First, translate your commas to spaces, and spaces to commas, then flip your coordinates from lat,long to long,lat (x,y). Here is a PostGIS function:
CREATE FUNCTION polygon_from_funny_format(latlon text) RETURNS geometry AS
$BODY$SELECT
ST_FlipCoordinates(('SRID=4326;POLYGON((' ||
translate($1, ', ', ' ,') || '))')::geometry);
$BODY$ LANGUAGE sql IMMUTABLE STRICT;
Now you can do:
SELECT polygon_from_funny_format('39.43,-80.29 39.46,-80.49 39.43,-80.52 39.46,-80.66 39.98,-80.76 40.07,-80.43 39.46,-79.91 39.39,-80.10 39.40,-80.11 39.39,-80.18 39.43,-80.29');
hey i need a way to get a formated number from my column decimal(23,2) NOT NULL DEFAULT '0.00'
in php i could use this function number_format('1111.00', 2, ',', '.');
it would return 1.111,00 (in Germany we use , to define decimal numbers)
how would i do this in mysql? with string replaces?
http://blogs.mysql.com/peterg/2009/04/
In Mysql 6.1 you will be able to do FORMAT(X,D [,locale_name] )
As in
SELECT format(1234567,2,’de_DE’);
For now this ability does not exist, though you MAY be able to set your locale in your database my.ini check it out.
With performance penalty and if you need todo it only in SQL you can use the FORMAT function and 3 REPLACE :
After the format replace the . with another char for example #, then replace the , with a . and then the chararacter you choose by a , which lead you for your example to 1.111,00
SELECT REPLACE(REPLACE(REPLACE(FORMAT("1111.00", 2), ".", "#"), ",", "."), "#", ",")
You can use
SELECT round(123.4566,2) -> 123.46
FORMAT(X,D) Formats the number X to a format like '#,###,###.##', rounded to D decimal places, and returns the result as a string. If D is 0, the result has no decimal point or fractional part.
SELECT FORMAT(12332.123456, 4);
-> '12,332.1235'
Antonio's answer
CONCAT(REPLACE(FORMAT(number,0),',','.'),',',SUBSTRING_INDEX(FORMAT(number,2),'.',-1))
is wrong; it may produce incorrect results!
For example, if "number" is 12345.67, the resulting string would be:
'12.346,67'
instead of
'12.345,67'
because FORMAT(number,0) rounds "number" up if fractional part is greater or equal than 0.5 (as it is in my example)!
What you COULD use is
CONCAT(REPLACE(FORMAT(FLOOR(number),0),',','.'),',',SUBSTRING_INDEX(FORMAT(number,2),'.',-1))
if your MySQL/MariaDB's FORMAT doesn't support "locale_name" (see MindStalker's post - Thx 4 that, pal). Note the FLOOR function I've added.
At least as far back as MySQL 5.5 you can use format:
SELECT FORMAT(123456789.123456789,2);
/* produces 123,456,789.12 */
SELECT FORMAT(123456789.123456789,2,'de_DE');
/*
produces 123.456.789,12
note the swapped . and , for the de_DE locale (German-Germany)
*/
From the MySQL docs:
https://dev.mysql.com/doc/refman/5.5/en/string-functions.html#function_format
Available locales are listed elsewhere in the docs:
https://dev.mysql.com/doc/refman/5.5/en/locale-support.html
CREATE DEFINER=`yourfunctionname`#`%` FUNCTION `money`(
`betrag` DECIMAL(10,2)
)
RETURNS varchar(128) CHARSET latin1
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
return(
select replace(format(cast(betrag as char),2),',',"'") as betrag
)
will creating a MySql-Function with this Code:
select replace(format(cast(amount as char),2),',',"'") as amount_formated
You need this:
CONCAT(REPLACE(FORMAT(number,0),',','.'),',',SUBSTRING_INDEX(FORMAT(number,2),'.',-1))