Switch coordinates (X with Y) in SDO_ORDINATE_ARRAY - oracle

I have a spatial data in my Oracle 11g table (a column with SDO_GEOMETRY as object type). My data is proprietary and I cannot show you them, but I will use some random data, it doesn't matter in this case. The table and its data has been created by external application and I cannot change the way they are written to the database.
The problem is that coordinates in SDO_ORDINATE_ARRAY are switched - all X values are where Y values should be and vice versa. Take a look at this example:
This is what I get:
SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1,1003,1),
SDO_ORDINATE_ARRAY(5,1, 8,1, 8,6, 5,7, 5,1)
And this is what I need (switching X and Y values in SDO_ORDINATE_ARRAY):
SDO_GEOMETRY(2003, NULL, NULL, SDO_ELEM_INFO_ARRAY(1,1003,1),
SDO_ORDINATE_ARRAY(1,5, 1,8, 6,8, 7,5, 1,5)
What I am looking for is a SELECT statement that returns a column with SDO_GEOMETRY from the example above, but with switched X and Y values in SDO_ORDINATE_ARRAY. I don't need to update the table because I want to send correct data to another external application.
I will be very thankful for pointing me any directions.

The following function will do what you want:
create or replace function swap_ordinates (g_in sdo_geometry)
return sdo_geometry
is
g_out sdo_geometry;
i integer;
begin
-- Check input geometry: we only work on 2D shapes
if substr(g_in.sdo_gtype,1,1) <> 2 then
raise_application_error (-20001,'Geometry must be 2D');
end if;
-- Initialize output geometry
g_out := g_in;
-- Swap ordinates in sdo_point
if g_in.sdo_point is not null then
g_out.sdo_point.x := g_in.sdo_point.y;
g_out.sdo_point.y := g_in.sdo_point.x;
end if;
-- Copy ordinates, swapping X and Y
if g_in.sdo_ordinates is not null then
for i in 1..g_in.sdo_ordinates.count/2 loop
g_out.sdo_ordinates ((i-1)*2+1) := g_in.sdo_ordinates ((i-1)*2+2); -- Y -> X
g_out.sdo_ordinates ((i-1)*2+2) := g_in.sdo_ordinates ((i-1)*2+1); -- X -> Y
end loop;
end if;
-- Return fixed geometry
return g_out;
end;
/
show errors
It takes a geometry object as input and returns a new one with the X and Y ordinates swapped. It also works for points (by swapping the X and Y in the SDO_TYPE property).
Note that it only works for plain 2D geometries. If the geometry is 3D or LRS (or both), then it fails with an exception.
Generalizing the function to work with 3D or more is left as an exercise to the reader.

Based on the info you give, here are some guidelines:
If the size of your geometires (numvertices) and table permit it and if this is something you will not do too often, you can use a gis client (e.g. QGIS) to read and transform the data.
Otherwise, you can go the sql way: Use sdo_util.getvertices to explode a polygon to its nodes, put its X Y ords in a temporary table and then swap them and compose the 'new' polygon. You can put all of this in a function and call it whenever you like - even put it in a trigger and automate your flow. I've used this approach many times on tables with 1-6 mil. rows and it's quite scalable. If you need more info, let me know... And remember to validate your polygons before and after!

Related

Reading a text file and constructing a matrix from it

I need to construct a matrix; a number of columns and rows are also in the first row of the matrix, I'll make an example so its more clearer.
4 3
1 2 3
5 6 7
9 10 8
1 11 13
Where m=4 (number of rows) and n=3 (number of columns)
This is an example of a text file. Is something like this even possible?
Program Feb;
const
max=100;
type
Matrix=array[1..max,1..max] of integer;
var datoteka:text;
m,n:integer;
counter:integer;
begin
assign(datoteka,'datoteka.txt');
reset(datoteka);
while not eoln(datoteka) do
begin
read(datoteka, m);
read(datoteka, n);
end;
repeat
read eoln(n)
until eof(datoteka)
write (m,n);
end.
My code isn't a big help, cause I don't know how to write it.
First, have a look at the code I wrote to do the task, and then look at my explanation below.
program Matrixtest;
uses
sysutils;
var
NoOfCols,
NoOfRows : Integer;
Source : TextFile;
Matrix : array of array of integer;
FileName : String;
Row,
Col : Integer; // for-loop iterators to access a single cell of the matrix
Value : Integer;
begin
// First, construct the name of the file defining the matrix
// This assumes that the file is in the same folder as this app
FileName := ExtractFilePath(ParamStr(0)) + 'MatrixDef.Txt';
writeln(FileName); // echo it back to the screen so we can see it
// Next, open the file
Assign(Source, FileName);
Reset(Source);
read(Source, NoOfRows, NoOfCols);
writeln('Cols: ', NoOfCols, 'Rows: ', NoOfRows);
SetLength(Matrix, NoOfCols, NoOfRows);
readln(source); // move to next line in file
// Next, read the array data
for Row := 1 to NoOfRows do begin
for Col := 1 to NoOfCols do begin
read(Source, Value);
Matrix[Col - 1, Row - 1] := Value;
end;
end;
// Display the array contents
for Row := 1 to NoOfRows do begin
for Col := 1 to NoOfCols do begin
writeln('Row: ', Row, ' contents', Matrix[Col - 1, Row - 1]);
end;
end;
Close(Source); // We're done with the file, so close it to release OS resources
readln; // this waits until you press a key, so you can read what's been displayed
end.
In your program, you can use a two-dimensional array to represent your matrix. Free Pascal supports multi-dimensional arrays; see https://wiki.lazarus.freepascal.org/Multidimensional_arrays for more information.
This is a complex task, so it helps to know how to do more basic things like reading an array of a size known at compile-time from a text file.
The wrinkle in this task is that you are supposed to read the dimensions (numbers of rows and columns) of the matrix at run-time from the file which contains the matrix's contents.
One inefficient way to do this would be to declare the matrix array with huge dimensions, larger than anything you would expect in practice, using the type of array declaration in the Wiki page linked above.
A better way is to use dynamic arrays, whose dimensions you can set at run-time. To use this, you need to know:
How to declare a dynamic array in Free Pascal
How to set the dimensions of the array at run-time, once you've picked them up from your matrix-definition file (hint: SetLength is the way to do this)
The fact that a Free Pascal dynamic array is zero-based
The easiest way of managing zero-based arrays is to write your code (in terms of Row and Column variables) as if the matrix were declared as array[1..NoOfRows, 1..NoOfColumns] and subtract one from the array indexes only when you actually access the array, as in:
Row := 3;
Column := 4;
Value := Matrix[Row - 1, Column - 1];

Exporting data into excel using iterative loop

I am doing an iterative calculation on maple and I want to store the resulting data (which comes in a column matrix) from each iteration into a specific column of an Excel file. For example, my data is
mydat||1:= <<11,12,13,14>>:
mydat||2:= <<21,22,23,24>>:
mydat||3:= <<31,32,33,34>>:
and so on.
I am trying to export each of them into an excel file and I want each data to be stored in consecutive columns of the same excel file. For example, mydat||1 goes to column A, mydat||2 goes to column B and so on. I tried something like following.
with(ExcelTools):
for k from 1 to 3 do
Export(mydat||k, "data.xlsx", "Sheet1", "A:C"): #The problem is selecting the range.
end do:
How do I select the range appropriately here? Is there any other method to export the data and store in the way that I explained above?
There are couple of ways to do this. The easiest is certainly to put all of your data into one data structure and then export that. For example:
mydat1:= <<11,12,13,14>>:
mydat2:= <<21,22,23,24>>:
mydat3:= <<31,32,33,34>>:
mydata := Matrix( < mydat1 | mydat2 | mydat3 > );
This stores your data in a Matrix where mydat1 is the first column, mydat2 is the second column, etc. With the data in this form, either ExcelTools:-Export or the more generic Export command will work:
ExcelTools:-Export( data, "data.xlsx" );
Export( "data.xlsx", data );
Now since you mention that you are doing an iterative calculation, you may want to write the results out column by column. Here's another method that doesn't involve the creation of another data structure to house the results. This does assume that the data in mydat"i" has been created before the loop.
for i to 3 do
ExcelTools:-Export( cat(`mydat`,i), "data.xlsx", 1, ["A1","B1","C1"][i] );
end do;
If you want to write the data out to a file as you are building it, then just do the Export call after the creation of each of the columns, i.e.
ExcelTools:-Export( mydat1, "data.xlsx", 1, "A1" );
Note that I removed the "||" characters. These are used in Maple for concatenation and caused some issues with the second method.

Inferring type of values returned by the SQLite3 shell tool

I am out of necessity using the SQLite3 shell tool to maintain a small database. I'm using -header -ascii flags, although this applies—as far as I can tell—to any of the output choices. I'm looking a way to avoid ambiguity over the type of any one value returned. Consider the following:
Create Table `things` (`number` Integer, `string` Text, `binary` Blob);
Insert Into `things` (`number`,`string`,`binary`) Values (4,'4',X'34');
Select * From `things`;
This returns (using caret notation):
number^_string^_binary^^4^_4^_4^^
As is evident, there is no way to infer the type of any of the '4' characters from the response alone as none of them have distinguishing delimiters.
Is there any way to coerce the inclusion of type metadata into the response?
I'd like to avoid:
Altering query statements to also include types as that would be obfuscatory and would be superfluous in the event I did switch interfaces;
Prefixing TEXT and BLOB values prior to insert as this would have to be uniform for all TEXT and BLOB interaction (in saying that, this is still my preferred choice should it come to that).
What I'm looking for is a switch of some kind that indicates type as part of SQLite's response, e.g.:
number^_string^_binary^^4^_'4'^_X'4'^^
number^_string^_binary^^4^_text:4'^blob:4^^
Or some variation thereof. Fundamental to this is the response alone contains enough information to discern the type and value of each element of that response (much in the same way sqlite3_column_type() allows in the SQLite Library API).
Update: I've refined this question since the first answer by #mike-sherrill-cat-recall to clarify expectations.
In SQLite, it doesn't always make sense to echo the data type of a column. SQLite doesn't have column-wise data types in the traditional sense. You can use typeof(X) in SQL to show the "datatype of the expression X".
sqlite> create table test (n integer, d decimal(8, 2));
sqlite> insert into test (n, d) values (8, 3.14);
sqlite> insert into test (n, d) values ('wibble', 'wibble');
Inserting text into an integer column succeeds.
sqlite> select n, typeof(n), d, typeof(d) from test;
n typeof(n) d typeof(d)
---------- ---------- ---------- ----------
8 integer 3.14 real
wibble text wibble text
You can concatenate anything you like--even producing caret notation--but it's kind of clumsy.
sqlite> select '(' || typeof(n) || ')^_' || n as caret_n from test;
caret_n
-------------------------
(integer)^_8
(text)^_wibble
SQLite Core Functions
The shell always converts printed values to strings. (That's what "print" means.)
If you don't want to add separate output columns for the types, you could use the quote function to output all values according to SQL syntax rules:
sqlite> with v(x) as (values (null), (1), (2.3), ('hello'), (x'00')) select quote(x) from v;
NULL
1
2.3
'hello'
X'00'

Are Oracle PL/SQL arrays indexed from 0 or from 1?

I have in front of me a piece of code like this:
FOR row IN 1..l_RowSet(1).count
LOOP
l_a_variable := l_RowSet(1)(row);
END LOOP;
l_RowSet is an ApEx type -- apex_plugin_util.t_column_value_list -- defined thus:
type t_column_value_list is table of wwv_flow_global.vc_arr2 index by pls_integer;
where wwv_flow_global.vc_arr2 is defined as
type vc_arr2 is table of varchar2(32767) index by binary_integer;
The vc_arr2 is passed back to my code from the apex_plugin_util.get_data function. The vc_arr2 is indexed by column number, not by row.
As best I can make out this means that the data is effectively stored in a 2D array, indexed by column and then by row.
When using the LOOP statement, would this be indexed from zero or from one? Because it seems to me that I ought to be able to make that LOOP redundant, ie:
l_a_variable := l_RowSet(1)(1);
But I'd need to know in advance whether to give 0 or 1 as the initial row.
I can't find a clear answer in the Oracle docs (unsurprisingly, "index" is a fairly widely-used term) and a look through SO doesn't show anybody else with the same question either.
An associative array isn't necessarily dense. There may be an element at index 0, there may be an element at index -1, there may be an element at index 1. Or you might have elements at indexes 17, 42, and 127. The code you posted implies that the associative array is dense and that the indexes start at 1.
In the specific case of apex_plugin_util.get_data the collection should be dense and should start at 1. If the loop is actually not doing anything other than what you posted, you could replace it by fetching the last element of l_RowSet(1), i.e.
l_a_variable := l_RowSet(1)(l_RowSet(1).count);

DataColumn Expression Divide By Zero

I'm using basic .net DataColumns and the associated Expression property.
I have an application which lets users define which columns to select from a database table. They can also add other columns which perform expressions on the data columns resulting in a custom grid of information.
The problem I have is when they have a calculation column along the lines of "(C2/C3)*100" where C2 and C3 are data columns and the value for C3 is zero. The old "divide by zero" issue.
The simple answer would be to convert the expression to "IIF(C3 = 0, 0, (C2/C3)*100)", however we don't expect the user to know to do that and at compile time I don't know what columns are defined. So I would have to programmatically determine which columns are being used in a division in order to construct the IIF clause. That could get quite tricky.
Is there another way to not throw an error and replace the result with 0 if a "Divide By Zero" error occurs?
Ok, I found a way. The key is to use Double and not Decimal for the column type, e.g. in the example above C3 should be a Double. This will result in a result of Infinity instead, which can be evaluated against using the expression as a whole.
E.g.
IIF(CONVERT(([C4] / [C3] )*100, 'System.String') = 'NaN' OR CONVERT(([C4] / [C3] )*100, 'System.String') = 'Infinity' OR CONVERT(([C4] / [C3] )*100, 'System.String') = '-Infinity', 0, ([C4] / [C3] )*100)
Decimal it seems doesn't provide that Infinity option.

Resources