using a variable to identify file in 'print -dpdf file_name' - octave

I am trying to use a formatted string to identify the file location when using 'print -dpdf file_name' to write a plot (or figure) to a file.
I've tried:
k=1;
file_name = sprintf("\'/home/user/directory to use/file%3.3i.pdf\'",k);
print -dpdf file_name;
but that only gets me a figure written to ~/file_name.pdf which is not what I want. I've tried several other approaches but I cannot find an approach that causes the the third term (file_name, in this example) to be evaluated. I have not found any other printing function that will allow me to perform a formatted write (the '-dpdf' option) of a plot (or figure) to a file.
I need the single quotes because the path name to the location where I want to write the file contains spaces. (I'm working on a Linux box running Fedora 24 updated daily.)
If I compute the file name using the line above, then cut and paste it into the print statement, everything works exactly as I wish it to. I've tried using
k=1;
file_name = sprintf("\'/home/user/directory to use/file%3.3i.pdf\'",k);
print ("-dpdf", '/home/user/directory to use/file001.pdf');
But simply switching to a different form of print statement doesn't solve the problem,although now I get an error message:
GPL Ghostscript 9.16: **** Could not open the file '/home/user/directory to use/file001.pdf' .
**** Unable to open the initial device, quitting.
warning: broken pipe

if you use foo a b this is the same as foo ("a", "b"). In your case you called print ("-dpdf", "file_name")
k = 1;
file_name = sprintf ("/home/user/directory to use/file%3.3i.pdf", k);
print ("-dpdf", file_name);

Observe:
>> k=1;
>> file_name = sprintf ('/home/tasos/Desktop/a folder with spaces in it/this is file number %3.3i.pdf', k)
file_name = /home/tasos/Desktop/a folder with spaces in it/this is file number 001.pdf
>> plot (1 : 10);
>> print (gcf, file_name, '-dpdf')
Tadaaa!
So yeah, no single quotes needed. The reason single quotes work when you're "typing it by hand" is because you're literally creating the string on the spot with the single quotes.
Having said that, it's generally a good idea when generating absolute paths to use the fullfile command instead. Have a look at it.

Tasos Papastylianou #TasosPapastylianou provided great help. My problem is now solved.

Related

error finding and uploading a file in octave

I tried converting my .csv file to .dat format and tried to load the file into Octave. It throws an error:
unable to find file filename
I also tried to load the file in .csv format using the syntax
x = csvread(filename)
and it throws the error:
'filename' undefined near line 1 column 13.
I also tried loading the file by opening it on the editor and I tried loading it and now it shows me
warning: load: 'filepath' found by searching load path
error: load: unable to determine file format of 'Salary_Data.dat'.
How can I load my data?
>> load Salary_Data.dat
error: load: unable to find file Salary_Data.dat
>> Salary_Data
error: 'Salary_Data' undefined near line 1 column 1
>> Salary_Data
error: 'Salary_Data' undefined near line 1 column 1
>> Salary_Data
error: 'Salary_Data' undefined near line 1 column 1
>> x = csvread(Salary_Data)
error: 'Salary_Data' undefined near line 1 column 13
>> x = csvread(Salary_Data.csv)
error: 'Salary_Data' undefined near line 1 column 13
>> load Salary_Data.dat
warning: load: 'C:/Users/vaith/Desktop\Salary_Data.dat' found by searching load path
error: load: unable to determine file format of 'Salary_Data.dat'
>> load Salary_Data.csv
warning: load: 'C:/Users/vaith/Desktop\Salary_Data.csv' found by searching load path
error: load: unable to determine file format of 'Salary_Data.csv'
Salary_Data.csv
YearsExperience,Salary
1.1,39343.00
1.3,46205.00
1.5,37731.00
2.0,43525.00
2.2,39891.00
2.9,56642.00
3.0,60150.00
3.2,54445.00
3.2,64445.00
3.7,57189.00
3.9,63218.00
4.0,55794.00
4.0,56957.00
4.1,57081.00
4.5,61111.00
4.9,67938.00
5.1,66029.00
5.3,83088.00
5.9,81363.00
6.0,93940.00
6.8,91738.00
7.1,98273.00
7.9,101302.00
8.2,113812.00
8.7,109431.00
9.0,105582.00
9.5,116969.00
9.6,112635.00
10.3,122391.00
10.5,121872.00
Ok, you've stumbled through a whole pile of issues here.
It would help if you didn't give us error messages without the commands that produced them.
The first message means you were telling Octave to open something called filename and it couldn't find anything called filename. Did you define the variable filename? Your second command and the error message suggests you didn't.
Do you know what Octave's working directory is? Is it the same as where the file is located? From the response to your load commands, I'd guess not. The file is located at C:/Users/vaith/Desktop. Octave's working directory is probably somewhere else.
(Try the pwd command and see what it tells you. Use the file browser or the cd command to navigate to the same location as the file. help pwd and help cd commands would also provide useful information.)
The load command, used as a command (load file.txt) can take an input that is or isn't defined as a string. A function format (load('file.txt') or csvread('file.txt')) must be a string input, hence the quotes around file.txt. So all of your csvread input commands thought you were giving it variable names, not filenames.
Last, the fact that load couldn't read your data isn't overly surprising. Octave is trying to guess what kind of file it is and how to load it. I assume you tried help load to see what the different command options are? You can give it different options to help Octave figure it out. If it actually is a csv file though, and is all numbers not text, then csvread might still be your best option if you use it correctly. help csvread would be good information for you.
It looks from your data like you have a header line that is probably confusing the load command. For data that simply formatted, the csvread command can bring in the data. It will replace your header text with zeros.
So, first, navigate to the location of the file:
>> cd C:/Users/vaith/Desktop
then open the file:
>> mydata = csvread('Salary_Data.csv')
mydata =
0.00000 0.00000
1.10000 39343.00000
1.30000 46205.00000
1.50000 37731.00000
2.00000 43525.00000
...
If you plan to reuse the filename, you can assign it to a variable, then open the file:
>> myfile = 'Salary_Data.csv'
myfile = Salary_Data.csv
>> mydata = csvread(myfile)
mydata =
0.00000 0.00000
1.10000 39343.00000
1.30000 46205.00000
1.50000 37731.00000
2.00000 43525.00000
...
Notice how the filename is stored and used as a string with quotation marks, but the variable name is not. Also, csvread converted non-numeric header data to 'zeros'. The help for csvread and dlmread show you how to change it to something other than zero, or to skip a certain number of rows. If you want to preserve the text, you'll have to use some other input function.

How can I write certain sections of text from different lines to multiple lines?

So I'm currently trying to use Python to transform large sums of data into a neat and tidy .csv file from a .txt file. The first stage is trying to get the 8-digit company numbers into one column called 'Company numbers'. I've created the header and just need to put each company number from each line into the column. What I want to know is, how do I tell my script to read the first eight characters of each line in the .txt file (which correspond to the company number) and then write them to the .csv file? This is probably very simple but I'm only new to Python!
So far, I have something which looks like this:
with open(r'C:/Users/test1.txt') as rf:
with open(r'C:/Users/test2.csv','w',newline='') as wf:
outputDictWriter = csv.DictWriter(wf,['Company number'])
outputDictWriter.writeheader()
rf = rf.read(8)
for line in rf:
wf.write(line)
My recommendation would be 1) read the file in, 2) make the relevant transformation, and then 3) write the results to file. I don't have sample data, so I can't verify whether my solution exactly addresses your case
with open('input.txt','r') as file_handle:
file_content = file_handle.read()
list_of_IDs = []
for line in file_content.split('\n')
print("line = ",line)
print("first 8 =", line[0:8])
list_of_IDs.append(line[0:8])
with open("output.csv", "w") as file_handle:
file_handle.write("Company\n")
for line in list_of_IDs:
file_handle.write(line+"\n")
The value of separating these steps is to enable debugging.

It is possible to obtain the mean of different files to make some computations with it after?

I have a code to calculate the mean of the first five values of each column of a file, for then use these values as a reference point for all set. The problem is that now I need to do the same but for many files. So I will need to obtain the mean of each file to then use these values again with the originals files. I have tried in this way but I obtain an error. Thanks.
%%% - Loading the file of each experiment
myfiles = dir('*.lvm'); % To load every file of .lvm
for i = 1:length(myfiles) % Loop with the number of files
files=myfiles(i).name;
mydata(i).files = files;
mydata(i).T = fileread(files);
arraymean(i) = mean(mydata(i));
end
The files that I need to compute are more or less like this:
Delta_X 3.000000 3.000000 3.000000
***End_of_Header***
X_Value C_P1N1 C_P1N2 C_P1N3
0.000000 -0.044945 -0.045145 -0.045705
0.000000 -0.044939 -0.045135 -0.045711
3.000000 -0.044939 -0.045132 -0.045706
6.000000 -0.044938 -0.045135 -0.045702
Your first line results in 'myfiles' being a structure array with components that you will find defined when you type 'help dir'. In particular, the names of all the files are contained in the structure element myfiles(i).name. To display all the file names, type myfiles.name. So far so good. In the for loop you use 'fileread', but fileread (see help fileread) returns the character string rather than the actual values. I have named your prototype .lvm file DinaF.lvm and I have written a very, very simple function to read the data in that file, by skipping the first three lines, then storing the following matrix, assumed to have 4 columns, in an array called T inside the function and arrayT in the main program
Here is a modified script, where a function read_lvm has been included to read your 'model' lvm file.
The '1' in the first line tells Octave that there is more to the script than just the following function: the main program has to be interpreted as well.
1;
function T=read_lvm(filename)
fid = fopen (filename, "r");
%% Skip by first three lines
for lhead=1:3
junk=fgetl(fid);
endfor
%% Read nrow lines of data, quit when file is empty
nrow=0;
while (! feof (fid) )
nrow=nrow + 1;
thisline=fscanf(fid,'%f',4);
T(nrow,1:4)=transpose(thisline);
endwhile
fclose (fid);
endfunction
## main program
myfiles = dir('*.lvm'); % To load every file of .lvm
for i = 1:length(myfiles) % Loop with the number of files
files=myfiles(i).name;
arrayT(i,:,:) = read_lvm(files);
columnmean(i,1:4)=mean(arrayT(i,:,:))
end
Now the tabular values associated with each .lvm file are in the array arrayT and the mean for that data set is in columnmean(i,1:4). If i>1 then columnmean would be an array, with each row containing the files for each lvm file. T
This discussion is getting to be too distant from the initial question. I am happy to continue to help. If you want more help, close this discussion by accepting my answer (click the swish), then ask a new question with a heading like 'How to read .lvm files in Octave'. That way you will get the insights from many more people.

How to get values from JSON file using AppleScript?

In reference to this question,
How to download and get values from JSON file using VBScript or batch file?
how to get the values from JSON file that looks like this,
["AA-BB-CC-MAKE-SAME.json","SS-ED-SIXSIX-TENSE.json","FF-EE-EE-EE-WW.json","ZS-WE-AS-FOUR-MINE.json","DD-RF-LATERS-LATER.json","FG-ER-DC-ED-FG.json"]
using AppleScript in MAC OS?
Here is part of VBScript code in Windows provided by Hackoo,
strJson = http.responseText
Result = Extract(strJson,"(\x22(.*)\x22)")
Arr = Split(Result,",")
For each Item in Arr
wscript.echo Item
Next
'******************************************
Function Extract(Data,Pattern)
Dim oRE,oMatches,Match,Line
set oRE = New RegExp
oRE.IgnoreCase = True
oRE.Global = True
oRE.Pattern = Pattern
set oMatches = oRE.Execute(Data)
If not isEmpty(oMatches) then
For Each Match in oMatches
Line = Line & Trim(Match.Value) & vbCrlf
Next
Extract = Line
End if
End Function
'******************************************
In MAC OS AppleScript I only need the code to get the values of the JSON file to a single array of string values. The above shown example above the VBScript is the how JSON file contents looks like.
Short answer: Unfortunately, AppleScript doesn't provide a built-in feature to parse JSON which is analogous to JavaScript's JSON.parse() method.
Below are a couple of solutions:
Solution 1: Requires a third party plug-in to be installed, which may not always be feasible.
Solution 2: Does not require any third party plug-in to be installed, and instead utilizes tools/features built-in to macOS as standard.
Solution 1:
If you have the luxury of being able to install a third-party plugin on your users systems then you can install JSON Helper for AppleScript (As suggested by #user3439894 in the comments).
Then use it in your AppleScript as follows:
set srcJson to read POSIX file (POSIX path of (path to home folder) & "Desktop/foobar.json")
tell application "JSON Helper" to set myList to read JSON from srcJson
Explanation:
On line 1 we read the contents of the .json file and assign it to the variable named srcJson.
Note You'll need to change the path part (i.e. Desktop/foobar.json) as necessary.
On line 2 we parse the contents using the JSON Helper plug-in. This assigns each item of the source JSON Array to a new AppleScript list. The resultant AppleScript list is assigned to a variable named myList.
Solution 2:
By utilizing tools built-in to macOS as standard, you can also do the following via AppleScript. This assumes that your JSON file is valid and contains a single Array only:
set TID to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
set myList to text items of (do shell script "tr ''\\\\n\\\\r'' ' ' <~/Desktop/foobar.json | sed 's/^ *\\[ *\"//; s/ *\" *\\] *$//; s/\" *, *\"/,/g;'")
set AppleScript's text item delimiters to TID
Note: you'll need to change the path part (i.e. ~/Desktop/foobar.json) as necessary.
Also, if your .json filename includes a space(s) you'll need to escape them with \\. For instance ~/Desktop/foo\\ bar.json
Explanation:
On line 1 AppleScript's current text item delimiters are assigned to a variable named TID.
On line 2 AppleScript's text item delimiters are set to a comma - this will help when extracting each individual value from the source JSON Array and assigning each value to a new AppleScript list.
On line 3 a shell script is executed via the do shell script command, which performs the following:
Reads the content of the source .json file via the part which reads ~/Desktop/foobar.json. This path currently assumes the file is named foobar.json and resides in your Desktop folder (You'll need to change this path to wherever your actual file exists).
The content of foobar.json is redirected, (note the < before the filepath), to tr (i.e. the part which reads: tr ''\\\\n\\\\r'' ' '). This translation will replace any newline characters which may exists in the contents of the source .json Array with space characters. This ensures the contents of foobar.json is transformed to one line.
Note: A JSON Array can contain newlines between each item and still be valid, so although the example JSON given in your question appears on one line - it is not a requirement of this solution as it will handle multi-line too.
The one line of text is then piped to sed's s command for further processing (i.e. the part which reads: | sed 's/^ *\\[ *\"//; s/ *\" *\\] *$//; s/\" *, *\"/,/g;').
The syntax of the s command is 's/regexp/replacement/flags'.
Let's breakdown each s command to further understand what is happening:
s/^ *\\[ *\"// removes the opening square bracket [, which may be preceded or followed by zero or more space characters, and the following double quote (i.e. the first occurrence) from the beginning of the string.
s/ *\" *\\] *$// removes the closing square bracket ], which may be preceded or followed by zero or more space characters, and the preceding double quote (i.e. the last occurrence) from the end of the string.
s/\" *, *\"/,/g replaces single commas, (which may be preceded with zero or more spaces, and/or followed by zero or more spaces) with a single comma.
The initial part on line 3 which reads; set myList to text items of ... utilizes text items to read the String into an AppleScript list using commas as delimiters to determine each item of the list. The resultant Array is assigned to a variable named myList.
On line 4 AppleScript's text item delimiters are restored to their original value.
Utilizing a variable for the source JSON filepath.
If you want to utilize a variable for the filepath to the source .json file then you can do something like this instead:
set srcFilePath to quoted form of (POSIX path of (path to home folder) & "Desktop/foobar.json")
set TID to AppleScript's text item delimiters
set AppleScript's text item delimiters to ","
set myList to text items of (do shell script "tr ''\\\\n\\\\r'' ' ' <" & srcFilePath & " | sed 's/^ *\\[ *\"//; s/ *\" *\\] *$//; s/\" *, *\"/,/g;'")
set AppleScript's text item delimiters to TID
Note This is very much the same as the first example. The notable differences are:
On the first line we assign the filepath to a variable named srcFilePath.
In the do shell script we reference the srcFilePath variable.
Additional note regarding JSON escaped special characters: Solution 2 preserves any JSON escaped special characters which may be present in the values of source JSON array. However, Solution 1 will interpret them.
Caveats Solution 2 produces unexpected results when an item in the source JSON array includes a comma because a comma is used as a text item delimiters.
How to get the values from JSON file that looks like this,
["AA-BB-CC-MAKE-SAME.json","SS-ED-SIXSIX-TENSE.json","FF-EE-EE-EE-WW.json","ZS-WE-AS-FOUR-MINE.json","DD-RF-LATERS-LATER.json","FG-ER-DC-ED-FG.json"]
If you actually mean what you wrote, and that the contents of the JSON file is that list of six strings in a single array, formatted on a single line, the simplest way is to treat it as text, trim the opening and closing square brackets, then delimit its fields at every occurrence of a ,. Finally, each individual text item can have the surrounding quotes trimmed as well.
Examining the VBScript, it looks like it uses a very similar process, albeit with regular expressions, which AppleScript doesn't feature but which aren't especially necessary in this simple situation.
Let's assume that the JSON array above is stored in a file on your desktop called "myfile.json". Then:
set home to the path to home folder
set f to the POSIX path of home & "Desktop/myfile.json"
set JSONstr to read POSIX file f
# Trim square brackets
set JSONstr to text 2 thru -2 of JSONstr
# Delimit text fields using comma
set the text item delimiters to ","
set Arr to the text items of JSONstr
# Trim quotes of each item in Arr
repeat with a in Arr
set contents of a to text 2 thru -2 of a
end repeat
# The final array
Arr
I only need the code to get the values of the JSON file to a single array of string values. The above shown example above the VBScript is the how JSON file contents looks like.
The variable Arr now contains the array (referred to as lists in AppleScript) of string values. You can access a particular item in it like this:
item 2 of Arr --> "SS-ED-SIXSIX-TENSE.json"
A More General Solution
I've decided to include a more advanced way to handle JSON in an AppleScript, partly because I've been doing a lot of JSON processing quite recently and this is all fresh on my event horizon; but also to demonstrate that, using AppleScriptObjC, parsing even very complex JSON data is not only possible, but quite simple.
I don't think you'll need it in this specific case, but it could come in useful for some future situation.
The script has three sections: it starts off importing the relevant Objective-C framework that gives AppleScript additional powers; then, I define the actual handler itself, called JSONtoRecord, which I describe below. Lastly, comes the bottom of the script where you can enter your code and do whatever you like with it:
use framework "Foundation"
use scripting additions
--------------------------------------------------------------------------------
property ca : a reference to current application
property NSData : a reference to ca's NSData
property NSDictionary : a reference to ca's NSDictionary
property NSJSONSerialization : a reference to ca's NSJSONSerialization
property NSString : a reference to ca's NSString
property NSUTF8StringEncoding : a reference to 4
--------------------------------------------------------------------------------
on JSONtoRecord from fp
local fp
set JSONdata to NSData's dataWithContentsOfFile:fp
set [x, E] to (NSJSONSerialization's ¬
JSONObjectWithData:JSONdata ¬
options:0 ¬
|error|:(reference))
if E ≠ missing value then error E
tell x to if its isKindOfClass:NSDictionary then ¬
return it as record
x as list
end JSONtoRecord
--------------------------------------------------------------------------------
###YOUR CODE BELOW HERE
#
#
set home to the path to home folder
set f to the POSIX path of home & "Desktop/myfile.json"
JSONtoRecord from f
--> {"AA-BB-CC-MAKE-SAME.json", "SS-ED-SIXSIX-TENSE.json", ¬
--> "FF-EE-EE-EE-WW.json", "ZS-WE-AS-FOUR-MINE.json", ¬
--> "DD-RF-LATERS-LATER.json", "FG-ER-DC-ED-FG.json"}
At the bottom of the script, I've called the JSONtoRecord handler, passing it the location of myfile.json. One of the benefits of this handler is that it doesn't matter whether the file is formatted all on one line, or over many lines. It can also handle complex, nested JSON arrays.
In those instances, what it returns is a native AppleScript record object, with all the JSON variables stored as property values in the record. Accessing the variables then becomes very simple.
This is actually exactly what the JSON Helper application that a couple of people have already mentioned does under the hood.
The one criterion (other than the JSON file containing valid JSON data) is that the path to the file is a posix path written in full, e.g. /Users/CK/Desktop/myfile.json, and not ~/Desktop/myfile.json or, even worse, Macintosh HD:Users:CK:Desktop:myfile.json.

Labels on Nodes and Relationships from a CSV file

I have problem when i want to add a label on a Node or to a Relatioship.
I do this in Neo4j with Cypher:
LOAD CSV WITH HEADERS FROM "file:c:/Users/Test/test.csv" AS line
CREATE (n:line.FROM)
and i get this error:
Invalid input '.': expected an identifier character, whitespace, NodeLabel, a property map, ')' or a relationship pattern (line 2, column 15 (offset: 99))
"CREATE (n:line.FROM)"
If there is not a possible way of doing this with the Cypher Language, can you recommend me an other way to do my job?
It is very important to find a solution on this problem even with a Cypher solution or any Java thing to do this job...
Depends on how dynamic you need it to be, for small variability:
LOAD CSV WITH HEADERS FROM "file:c:/Users/Test/test.csv" AS line
WHERE line.FROM = "Foo"
CREATE (n:Foo)
From Java you can use node.addLabel(DynamicLabel.label(line.from))
Otherwise you can look into my neo4j-shell-tools, which allow dynamic labels and rel-types: with #{FROM}.
see: https://github.com/jexp/neo4j-shell-tools#cypher-import
Thank you all for your answers but none of them helped me to solve my problem.
I found a solution to do exactly what i wanted. The solution was the Neo4jImporter tool (Link from official manual: Neo4jImporter tool Manual ) and not Cypher language nor Java.
So here is an example of what i have done and worked for me
A test.csv file contains the "PropertyTest" and ":LABEL". Firstly it creates one node with the label "TEST" and after the creation it adds the "proptest" property on the "TEST" node. So to add a Label on your node you use :LABEL and to add a Property on the same node you add any name you want as a header in .csv file.
Example of test.csv file:
PropertyTest,:LABEL
proptest,TEST
For windows i've done the Neo4jImport.bat command as it is described in the manual page of Neo4j.You can found the Neo4jImport.bat in Windows at "C:\Program Files\Neo4j Community\bin" and you run it from command line (cmd).
In details i opened the cmd, i followed the path to Neo4jImport.bat and finaly i wrote:
Neo4jImport.bat --into path-to-save-your-neo4j-database --nodes path-to-your-csv\test.csv
--delimiter ","
The default delimiter of Neo4jImporter is the "," but you can change it. For example if your information in .csv file is seperated with tab you can do the following:
Neo4jImport.bat --into path-to-save-your-neo4j-database --nodes path-to-your-csv\test.csv
--delimiter "TAB"
That was the way that i loaded dynamically a whole model of almost 2.000 nodes with different Labels and Properties.
Keep in mind from the manual that you can add as many labels and as many properties you want on a node by adding to your csv more headers
Example of two Labels in a node:
PropertyTest,:LABEL,:LABEL
proptest,TEST,SECOND_LABEL
Example of Neo4jImport.bat for two Labels and comma seperated CSV file:
Neo4jImport.bat --into path-to-save-your-neo4j-database --nodes path-to-your-csv\test.csv
--delimiter ","
I hope that you will find it useful to this certain problem of Labels from .csv files and please read the official manual, it helped me a lot to find a solution for my problem.
Below is the way for two csv files MIP_nodes.csv and MIP_edges.csv:
//Load csv data into the database - with dynamic label(s)
WITH "file:///MIP_nodes.csv" AS uri
LOAD CSV WITH HEADERS FROM uri AS row
WITH * WHERE row.label <> ""
call apoc.merge.node ([row.label],{nodeId:row.nodeId, name: row.name, type: row.type, created: row.created, property1: row.property1, property2: row.property2})
YIELD node as n1
//RETURN n1
WITH * WHERE row.label = ""
call apoc.merge.node (['DefaultNode'],{nodeId:row.nodeId, name: row.name, type: row.type, created: row.created, property1: row.property1, property2: row.property2})
YIELD node as n2
RETURN n1, n2
//Load csv data into the database - with dynamic relationship(s)
//:auto USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM 'file:///MIP_edges.csv' AS row
MATCH (s)
WHERE s.nodeId = row.sourceId
//RETURN s
MATCH (d)
WHERE d.nodeId = row.destinationId
//RETURN d
CALL apoc.merge.relationship(s, row.label,{type:row.type, created: row.created, property1: row.property1, property2: row.property2},{}, d,{})
YIELD rel
//REMOVE rel.noOp;
RETURN rel;