Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion integration_tests/test_client_integrated.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ def teststack2(render, render_example_tilespec_and_transforms):
renderapi.stack.delete_stack(stack, render=render)



def test_tile_pair_client(render, teststack, **kwargs):
zvalues = np.array(renderapi.stack.get_z_values_for_stack(
teststack, render=render))
Expand All @@ -196,6 +195,21 @@ def test_tile_pair_client(render, teststack, **kwargs):
assert isinstance(tilepairjson, dict)
assert len(tilepairjson['neighborPairs']) > 3

multitpjson_tmp, multitpfiles_tmp = renderapi.client.tilePairClient(
teststack, np.min(zvalues), np.max(zvalues), outjson=None,
render=render, maxPairsPerFile=2, return_jsonfiles=True, **kwargs)

with tempfile.NamedTemporaryFile(
suffix=".json", mode='r', delete=False) as f:
outjson_specified = f.name

multitpjson, multitpfiles = renderapi.client.tilePairClient(
teststack, np.min(zvalues), np.max(zvalues), outjson=outjson_specified,
render=render, maxPairsPerFile=2, return_jsonfiles=True, **kwargs)

assert len(multitpjson["neighborPairs"]) == len(tilepairjson["neighborPairs"]) == len(multitpjson_tmp["neighborPairs"])
assert len(multitpfiles) == len(multitpfiles_tmp) == len(tilepairjson["neighborPairs"]) // 2


@pytest.mark.parametrize("bounds,raises", [
({}, True),
Expand Down
79 changes: 74 additions & 5 deletions renderapi/client/client_calls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import json
import os
import re
import subprocess
import tempfile

Expand Down Expand Up @@ -135,6 +136,11 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
excludeSameSectionNeighbors=None,
excludePairsInMatchCollection=None,
minx=None, maxx=None, miny=None, maxy=None,
useRowColPositions=None, existingMatchOwner=None,
minExistingMatchCount=None, onlyIncludeTilesFromStack=None,
onlyIncludeTilesNearTileIdsJson=None, maxPairsPerFile=None,
return_jsondata=True,
return_jsonfiles=False,
subprocess_mode=None,
host=None, port=None, owner=None, project=None,
client_script=None, memGB=None,
Expand Down Expand Up @@ -191,11 +197,29 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
minimum y bound from which tile 'p' is selected
maxy : float
maximum y bound from wich tile 'p' is selected
useRowColPositions : bool
whether to use raster positions for neighbor analysis rather
than radial cutoff
existingMatchOwner : str
owner for the excludePairsInMatchCollection collection
minExistingMatchCount : int
minimum match threshold to exclude pairs
present in excludePairsInMatchCollection collection
onlyIncludeTilesFromStack : str
only include tiles which exist in this stack
onlyIncludeTilesNearTileIdsJson : str
path to json listing tileIds which should be paired
maxPairsPerFile : int
maximum neighborPairs per file. Generating more pairs than
this number (default 100000) will generate additional json
files with a p[0-9]+ suffix on the root.

Returns
-------
:obj:`list` of :obj:`dict`
:obj:`list` of :obj:`dict`, optional
list of tilepairs
:obj:`list` of string, optional
list of json files containing tilepairs
"""
if outjson is None:
with tempfile.NamedTemporaryFile(
Expand All @@ -219,6 +243,18 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
'--excludeSameSectionNeighbors') +
get_param(excludePairsInMatchCollection,
'--excludePairsInMatchCollection') +
get_param(useRowColPositions,
'--useRowColPositions') +
get_param(existingMatchOwner,
'--existingMatchOwner') +
get_param(minExistingMatchCount,
'--minExistingMatchCount') +
get_param(onlyIncludeTilesFromStack,
"--onlyIncludeTilesFromStack") +
get_param(onlyIncludeTilesNearTileIdsJson,
'--onlyIncludeTilesNearTileIdsJson') +
get_param(maxPairsPerFile,
'--maxPairsPerFile') +
['--toJson', outjson] +
get_param(minx, '--minX') + get_param(maxx, '--maxX') +
get_param(miny, '--minY') + get_param(maxy, '--maxY'))
Expand All @@ -228,12 +264,45 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
subprocess_mode=subprocess_mode,
add_args=argvs, **kwargs)

with open(outjson, 'r') as f:
jsondata = json.load(f)
# We create the jsonfile, so if it is empty it could be multiple
if not os.path.isfile(outjson) or os.stat(outjson).st_size == 0:
if os.path.isfile(outjson):
# outjson we created is empty, so remove it
os.remove(outjson)

outbn_root, outbn_ext = os.path.splitext(os.path.basename(outjson))
outbn_pattern = "{root}_p[0-9]+{ext}".format(
root=outbn_root, ext=outbn_ext)
jsonfiles = [
os.path.join(os.path.dirname(outjson), bn)
for bn in os.listdir(os.path.dirname(outjson))
if re.match(outbn_pattern, bn)]
else:
jsonfiles = [outjson]

if return_jsondata:
jsondata_list = []
for jsonfile in jsonfiles:
with open(jsonfile, 'r') as f:
jsondata_list.append(json.load(f))
if len({d["renderParametersUrlTemplate"] for d in jsondata_list}) != 1: # pragma: no cover
raise ValueError(
"Found tilepair files with disparate "
"renderParametersUrlTemplate values. "
"Maybe there are additional files "
"matching the outjson pattern?")
pairdata = [i for l in (d["neighborPairs"] for d in jsondata_list)
for i in l]
jsondata = dict(jsondata_list[0], **{"neighborPairs": pairdata})

if delete_json:
os.remove(outjson)
return jsondata
for jsonfile in jsonfiles:
os.remove(jsonfile)

return ((jsondata, jsonfiles) if return_jsonfiles and return_jsondata
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the case of delete_json does it make sense to return paths to files that aren't there?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Practically speaking I don't think so, but since return_jsonfiles defaults to False it seems like we should keep the outputs explicit. Returning an empty list (i.e. just return the files you can expect to find) might work too, and I don't have a strong preference.

else jsondata if return_jsondata
else jsonfiles if return_jsonfiles
else None)


@renderclientaccess
Expand Down