コード例 #1
0
ファイル: cameraGeom.py プロジェクト: dr-guangtou/hs_hsc
    def testCamera(self):
        """Test if we can build a Camera out of Rafts"""

        #print >> sys.stderr, "Skipping testCamera"; return

        cameraInfo = {"ampSerial" : CameraGeomTestCase.ampSerial}
        camera = cameraGeomUtils.makeCamera(self.geomPolicy, cameraInfo=cameraInfo)

        if display:
            cameraGeomUtils.showCamera(camera, )
            ds9.incrDefaultFrame()

        if False:
            print cameraGeomUtils.describeCamera(camera)

        self.assertEqual(camera.getAllPixels().getWidth(), cameraInfo["width"])
        self.assertEqual(camera.getAllPixels().getHeight(), cameraInfo["height"])

        name = "R:1,0"
        self.assertEqual(camera.findDetector(cameraGeom.Id(name)).getId().getName(), name)

        self.assertEqual(camera.getSize().getMm()[0], cameraInfo["widthMm"])
        self.assertEqual(camera.getSize().getMm()[1], cameraInfo["heightMm"])

        #
        # Test mapping pixel <--> mm
        #
        for ix, iy, x, y in [(102, 500, -3.12, 2.02),
                             (152, 525, -2.62, 2.27),
                             (714, 500,  3.12, 2.02),
                             ]:
            pix = afwGeom.PointD(ix, iy) # wrt raft LLC
            pos = cameraGeom.FpPoint(x, y) # center of pixel wrt raft center
            posll = cameraGeom.FpPoint(x, y) # llc of pixel wrt raft center

            # may need to restructure this since adding FpPoint
            if False:
                self.assertEqual(camera.getPixelFromPosition(pos), pix)

            # there is no unique mapping from a pixel to a focal plane position
            #  ... the pixel could be on any ccd
            if False:
                self.assertEqual(camera.getPositionFromPixel(pix).getMm(), posll.getMm())
            
        # Check that we can find an Amp in the bowels of the camera
        ccdName = "C:1,0"
        amp = cameraGeomUtils.findAmp(camera, cameraGeom.Id(ccdName), 1, 2)
        self.assertFalse(amp is None)
        self.assertEqual(amp.getId().getName(), "ID7")
        self.assertEqual(amp.getParent().getId().getName(), ccdName)
コード例 #2
0
ファイル: cameraGeom.py プロジェクト: dr-guangtou/hs_hsc
    def testRaft(self):
        """Test if we can build a Raft out of Ccds"""

        #print >> sys.stderr, "Skipping testRaft"; return
        raftId = cameraGeom.Id("Raft")
        raftInfo = {"ampSerial" : CameraGeomTestCase.ampSerial}
        raft = cameraGeomUtils.makeRaft(self.geomPolicy, raftId, raftInfo=raftInfo)

        if display:
            cameraGeomUtils.showRaft(raft)
            ds9.incrDefaultFrame()

        if False:
            print cameraGeomUtils.describeRaft(raft)

        self.assertEqual(raft.getAllPixels().getWidth(), raftInfo["width"])
        self.assertEqual(raft.getAllPixels().getHeight(), raftInfo["height"])

        name = "C:0,2"
        self.assertEqual(raft.findDetector(cameraGeom.Id(name)).getId().getName(), name)

        self.assertEqual(raft.getSize().getMm()[0], raftInfo["widthMm"])
        self.assertEqual(raft.getSize().getMm()[1], raftInfo["heightMm"])
        #
        # Test mapping pixel <--> mm
        #
        ps = raft.getPixelSize()
        for ix, iy, x, y in [(102, 500, -1.01,  2.02),
                             (306, 100,  1.01, -2.02),
                             (306, 500,  1.01,  2.02),
                             (356, 525,  1.51,  2.27),
                             ]:
            pix = afwGeom.Point2I(ix, iy) # wrt raft LLC
            #position of pixel center
            pos = cameraGeom.FpPoint(x+ps/2., y+ps/2.) # wrt raft center
            #position of pixel lower left corner which is returned by getPositionFromPixel()
            posll = cameraGeom.FpPoint(x, y) # wrt raft center

            rpos = raft.getPixelFromPosition(pos)
            rpos = afwGeom.PointI(int(rpos.getX()), int(rpos.getY()))
            # need to rework cameraGeom since FpPoint changes.  disable this for now
            if False:
                self.assertEqual(rpos, pix)

            # this test is no longer meaningful as pixel is specific to a detector xy0
            if False:
                self.assertEqual(raft.getPositionFromPixel(afwGeom.Point2D(pix[0], pix[1])).getMm(),
                                 posll.getMm())
コード例 #3
0
def makeCamera(name="DECam"):
    camera = cameraGeom.Camera(cameraGeom.Id(name), 62, 1)

    for i in range(62):
        if i > 31:
            dewarName = "S%d" % (62 - i + 1)
        else:
            dewarName = "S%d" % (i + 1)
        camera.addDetector(afwGeom.PointI(i, 0),
                           cameraGeom.FpPoint(25.4 * 2.5 * (2.5 - i), 0.0),
                           cameraGeom.Orientation(0), makeRaft(dewarName))

    return camera
コード例 #4
0
    def setUp(self):
        self.prynt = False

        nx, ny = 6001, 8001
        pixelSize = 1.0  # mm
        allPixels = afwGeom.BoxI(afwGeom.PointI(0, 0), afwGeom.ExtentI(nx, ny))
        self.det = cameraGeomUtils.makeDefaultCcd(allPixels,
                                                  pixelSize=pixelSize)
        self.det.setCenter(cameraGeom.FpPoint(int(0.5 * nx), int(0.5 * ny)))

        # try the suprimecam numbers
        self.coeffs = [
            0.0, 1.0, 7.16417e-08, 3.03146e-10, 5.69338e-14, -6.61572e-18
        ]

        self.xs = [0.0, 1000.0, 5000.0]
        self.ys = [0.0, 1000.0, 4000.0]
コード例 #5
0
ファイル: cameraGeom.py プロジェクト: dr-guangtou/hs_hsc
    def testSortedCcds(self):
        """Test if the Ccds are sorted by ID after insertion into a Raft"""

        raft = cameraGeom.Raft(cameraGeom.Id(), 8, 1)
        Col = 0
        for serial in [7, 0, 1, 3, 2, 6, 5, 4]:
            ccd = cameraGeom.Ccd(cameraGeom.Id(serial))
            raft.addDetector(afwGeom.Point2I(Col, 0), cameraGeom.FpPoint(afwGeom.Point2D(0, 0)),
                             cameraGeom.Orientation(0), ccd)
            Col += 1
        #
        # Check that CCDs are sorted by Id
        #
        serials = []
        for ccd in raft:
            serials.append(ccd.getId().getSerial())

        self.assertEqual(serials, sorted(serials))
コード例 #6
0
def main(butler, visits, fields, fieldRadius, showCCDs=False, aitoff=False, alpha=0.2,
         byFilter=False, byVisit=False, title="", verbose=False):
    camera = butler.get("camera")

    ra, dec = [], []
    filters = {}
    _visits = visits; visits = []
    for v in _visits:
        try:
            exp = butler.get("raw", visit=v, ccd=49)
        except RuntimeError, e:
            if verbose:
                print >> sys.stderr, e
            continue

        ccd = exp.getDetector()

        xy = ccd.getPixelFromPosition(afwCG.FpPoint(0,0))
        sky = exp.getWcs().pixelToSky(xy)
        visits.append(v)
        ra.append( sky[0].asDegrees())
        dec.append(sky[1].asDegrees())
        filters[v] = exp.getFilter().getName()
コード例 #7
0
    def setUp(self):
        self.x0, self.y0 = 0, 0
        self.nx, self.ny = 512, 512  #2048, 4096
        self.sky = 100.0
        self.nObj = 100

        # make a distorter
        # This is a lot of distortion ... from circle r=1, to ellipse with a=1.3 (ie. 30%)
        # For suprimecam, we expect only about 5%
        self.distCoeffs = [0.0, 1.0, 2.0e-04, 3.0e-8]
        lanczosOrder = 3
        coefficientsDistort = True
        self.distorter = cameraGeom.RadialPolyDistortion(
            self.distCoeffs, coefficientsDistort, lanczosOrder)

        # make a detector
        self.detector = cameraUtils.makeDefaultCcd(
            afwGeom.Box2I(afwGeom.Point2I(0, 0),
                          afwGeom.Extent2I(self.nx, self.ny)))
        self.detector.setDistortion(self.distorter)
        self.detector.setCenter(cameraGeom.FpPoint(
            255.5, 255.5))  # move boresight from center to 0,0

        if False:
            for x, y in [(0, 0), (0, 511), (511, 0), (511, 511)]:
                p = afwGeom.Point2D(x, y)
                iqq = self.distorter.distort(p, geomEllip.Quadrupole(),
                                             self.detector)
                print x, y, geomEllip.Axes(iqq)
                print self.detector.getPositionFromPixel(p).getMm()

        print "Max distortion on this detector: ", self.distorter.computeMaxShear(
            self.detector)

        # detection policies
        self.detConfig = measAlg.SourceDetectionConfig()

        # measurement policies
        self.measSrcConfig = measAlg.SourceMeasurementConfig()

        # psf star selector
        starSelectorFactory = measAlg.starSelectorRegistry["secondMoment"]
        starSelectorConfig = starSelectorFactory.ConfigClass()
        starSelectorConfig.fluxLim = 5000.0
        starSelectorConfig.histSize = 32
        starSelectorConfig.clumpNSigma = 1.0
        starSelectorConfig.badFlags = []
        self.starSelector = starSelectorFactory(starSelectorConfig)

        # psf determiner
        psfDeterminerFactory = measAlg.psfDeterminerRegistry["pca"]
        psfDeterminerConfig = psfDeterminerFactory.ConfigClass()
        width, height = self.nx, self.ny
        nEigenComponents = 3
        psfDeterminerConfig.sizeCellX = width // 3
        psfDeterminerConfig.sizeCellY = height // 3
        psfDeterminerConfig.nEigenComponents = nEigenComponents
        psfDeterminerConfig.spatialOrder = 1
        psfDeterminerConfig.kernelSizeMin = 31
        psfDeterminerConfig.nStarPerCell = 0
        psfDeterminerConfig.nStarPerCellSpatialFit = 0  # unlimited
        self.psfDeterminer = psfDeterminerFactory(psfDeterminerConfig)
コード例 #8
0
def main(butler,
         visits,
         fields,
         fieldRadius,
         showCCDs=False,
         aitoff=False,
         alpha=0.2,
         byFilter=False,
         byVisit=False,
         title="",
         verbose=False):
    ra, dec = [], []
    filters = {}
    _visits, visits = visits, []
    for v in _visits:
        try:
            exp = butler.get("raw", visit=v, ccd=49)
        except RuntimeError as e:
            if verbose:
                print(e, file=sys.stderr)
            continue

        ccd = exp.getDetector()

        xy = ccd.getPixelFromPosition(afwCG.FpPoint(0, 0))
        sky = exp.getWcs().pixelToSky(xy)
        visits.append(v)
        ra.append(sky[0].asDegrees())
        dec.append(sky[1].asDegrees())
        filters[v] = exp.getFilter().getName()

    plt.clf()
    if aitoff:
        axes = plt.gcf().add_axes((0.1, 0.1, 0.85, 0.80), projection="aitoff")
        axes.grid(1)
    else:
        axes = plt.gca()
        axes.set_aspect('equal')

    ctypes = dict(
        BIAS="orange",
        DARK="cyan",
    )
    if byFilter:
        ctypeFilters = dict(
            g="green",
            r="red",
            r1="orange",
            i="magenta",
            z="brown",
            y="black",
            nb0921='darkgray',
        )
    else:
        ctypeKeys = {}
        colors = list("rgbcmyk") + ["orange", "brown", "orchid"
                                    ]  # colours for ctypeKeys

    if aitoff:
        fieldRadius = np.radians(fieldRadius)
        dec = np.radians(dec)
        ra = np.array(ra)
        ra = np.radians(np.where(ra > 180, ra - 360, ra))

    plots, labels = [], []
    for v, r, d in zip(visits, ra, dec):
        field = fields.get(v)
        if verbose:
            print("Drawing %s %s         \r" % (v, field), end=' ', flush=True)

        if byFilter:
            facecolor = ctypes.get(field, ctypeFilters.get(filters[v], "gray"))
        else:
            key = v if byVisit else field
            if key not in ctypeKeys:
                ctypeKeys[key] = colors[len(ctypeKeys) % len(colors)]
            facecolor = ctypeKeys[key]

        circ = Circle(xy=(r, d),
                      radius=fieldRadius,
                      fill=False if showCCDs else True,
                      facecolor=facecolor,
                      alpha=alpha)
        axes.add_artist(circ)

        if showCCDs:
            pathCodes = [
                Path.MOVETO,
                Path.LINETO,
                Path.LINETO,
                Path.LINETO,
                Path.CLOSEPOLY,
            ]

            for ccd in butler.queryMetadata("raw", "visit", ["ccd"], visit=v):
                try:
                    md = butler.get("raw_md", visit=v, ccd=ccd)
                except RuntimeError as e:
                    if verbose:
                        print(e, file=sys.stderr)
                    continue

                width, height = md.getScalar("NAXIS1"), md.getScalar("NAXIS2")
                wcs = afwGeom.makeSkyWcs(md)

                verts = []
                for p in [(0, 0), (width, 0), (width, height), (0, height)]:
                    sky = wcs.pixelToSky(afwGeom.PointD(*p))
                    verts.append([sky[0].asDegrees(), sky[1].asDegrees()])
                verts.append((0, 0))  # dummy

                axes.add_patch(
                    PathPatch(Path(verts, pathCodes),
                              alpha=alpha,
                              facecolor=facecolor))

        if byFilter:
            key = filters[v]
        else:
            key = v if byVisit else field

        if not labels.count(key):
            plots.append(Circle((0, 0), facecolor=facecolor))
            labels.append(key)

    plt.legend(plots, labels, loc='best',
               bbox_to_anchor=(0, 1.02, 1, 0.102)).draggable()

    if not aitoff:
        raRange = np.max(ra) - np.min(ra) + 1.2 * fieldRadius
        decRange = np.max(dec) - np.min(dec) + 1.2 * fieldRadius
        raRange *= np.cos(np.radians(np.mean(dec)))

        plt.xlim(0.5 * (np.max(ra) + np.min(ra)) + raRange * np.array((1, -1)))
        plt.ylim(0.5 * (np.max(dec) + np.min(dec)) +
                 decRange * np.array((-1, 1)))

    plt.xlabel("ra")
    plt.ylabel("dec")

    return plt
コード例 #9
0
ファイル: showCamera.py プロジェクト: rnikutta/afw
def main(camera, sample=20, showDistortion=True):
    if True:
        fig = plt.figure(1)
        fig.clf()
        ax = fig.add_axes((0.1, 0.1, 0.8, 0.8))

        title = camera.getId().getName()
        if showDistortion:
            title += ' (Distorted)'

        ax.set_title(title)
    else:
        fig = None

    if showDistortion:
        dist = camera.getDistortion()

    for raft in camera:
        raft = cameraGeom.cast_Raft(raft)
        for ccd in raft:
            if False and ccd.getId().getSerial() not in (0, 3):
                continue

            ccd = cameraGeom.cast_Ccd(ccd)
            ccd.setTrimmed(True)

            width, height = ccd.getAllPixels(True).getDimensions()

            corners = ((0.0, 0.0), (0.0, height), (width, height),
                       (width, 0.0), (0.0, 0.0))
            for (x0, y0), (x1, y1) in zip(corners[0:4], corners[1:5]):
                if x0 == x1 and y0 != y1:
                    yList = numpy.linspace(y0, y1, num=sample)
                    xList = [x0] * len(yList)
                elif y0 == y1 and x0 != x1:
                    xList = numpy.linspace(x0, x1, num=sample)
                    yList = [y0] * len(xList)
                else:
                    raise RuntimeError("Should never get here")

                xOriginal = []
                yOriginal = []
                xDistort = []
                yDistort = []
                for x, y in zip(xList, yList):
                    position = ccd.getPositionFromPixel(afwGeom.Point2D(
                        x, y))  # focal plane position

                    xOriginal.append(position.getMm().getX())
                    yOriginal.append(position.getMm().getY())

                    if not showDistortion:
                        continue

                    # Calculate offset (in CCD pixels) due to distortion
                    distortion = dist.distort(afwGeom.Point2D(x, y),
                                              ccd) - afwGeom.Extent2D(x, y)

                    # Calculate the distorted position
                    distorted = position + cameraGeom.FpPoint(
                        distortion) * ccd.getPixelSize()

                    xDistort.append(distorted.getMm().getX())
                    yDistort.append(distorted.getMm().getY())

                if fig:
                    ax.plot(xOriginal, yOriginal, 'k-')
                    if showDistortion:
                        ax.plot(xDistort, yDistort, 'r-')

            if fig:
                x, y = ccd.getPositionFromPixel(
                    afwGeom.Point2D(width / 2, height / 2)).getMm()
                ax.text(x, y, ccd.getId().getSerial(), ha='center')

    if fig:
        plt.show()
コード例 #10
0
ファイル: cameraGeom.py プロジェクト: dr-guangtou/hs_hsc
    def testCcd(self):
        """Test if we can build a Ccd out of Amps"""

        #print >> sys.stderr, "Skipping testCcd"; return

        ccdId = cameraGeom.Id("CCD")
        ccdInfo = {"ampSerial" : CameraGeomTestCase.ampSerial}
        ccd = cameraGeomUtils.makeCcd(self.geomPolicy, ccdId, ccdInfo=ccdInfo)
        if display:
            cameraGeomUtils.showCcd(ccd)
            ds9.incrDefaultFrame()
            trimmedImage = cameraGeomUtils.makeImageFromCcd(ccd, isTrimmed=True)
            cameraGeomUtils.showCcd(ccd, trimmedImage, isTrimmed=True)
            ds9.incrDefaultFrame()

        for i in range(2):
            self.assertEqual(ccd.getSize().getMm()[i],
                             ccdInfo["pixelSize"]*ccd.getAllPixels(True).getDimensions()[i])

        self.assertEqual(ccd.getId().getName(), ccdInfo["name"])
        self.assertEqual(ccd.getAllPixels().getWidth(), ccdInfo["width"])
        self.assertEqual(ccd.getAllPixels().getHeight(), ccdInfo["height"])
        self.assertEqual([a.getId().getSerial() for a in ccd],
                         range(ccdInfo["ampIdMin"], ccdInfo["ampIdMax"] + 1))

        id = cameraGeom.Id("ID%d" % ccdInfo["ampIdMax"])
        self.assertTrue(ccd.findAmp(id), id)

        self.assertEqual(ccd.findAmp(afwGeom.Point2I(10, 10)).getId().getSerial(), ccdInfo["ampIdMin"])

        self.assertEqual(ccd.getAllPixels().getMin(),
                         ccd.findAmp(afwGeom.Point2I(10, 10)).getAllPixels().getMin())

        self.assertEqual(ccd.getAllPixels().getMax(),
                         ccd.findAmp(afwGeom.Point2I(ccdInfo["width"] - 1,
                                                            ccdInfo["height"] - 1)).getAllPixels().getMax())
        ps = ccd.getPixelSize()
        #
        # Test mapping pixel <--> mm.  Use a pixel at the middle of the top of the CCD
        #
        pix = afwGeom.Point2D(99.5, 203.5)            # wrt bottom left
        pos = cameraGeom.FpPoint(0.00, 1.02)             # pixel center wrt CCD center
        posll = cameraGeom.FpPoint(0.00, 1.02)           # llc of pixel wrt CCD center
        #
        # Map pix into untrimmed coordinates
        #
        amp = ccd.findAmp(afwGeom.Point2I(int(pix[0]), int(pix[1])))
        corrI = amp.getDataSec(False).getMin() - amp.getDataSec(True).getMin()
        corr = afwGeom.Extent2D(corrI.getX(), corrI.getY())
        pix += corr
        
        self.assertEqual(amp.getDiskCoordSys(), cameraGeom.Amp.AMP)
        self.assertEqual(ccd.getPixelFromPosition(pos) + corr, pix)
        #
        # Trim the CCD and try again
        #
        trimmedImage = trimCcd(ccd)

        if display:
            ds9.mtv(trimmedImage, title='Trimmed')
            cameraGeomUtils.showCcd(ccd, trimmedImage)
            ds9.incrDefaultFrame()

        a = ccd.findAmp(cameraGeom.Id("ID%d" % ccdInfo["ampIdMin"]))
        self.assertEqual(a.getDataSec(), afwGeom.Box2I(afwGeom.Point2I(0, 0),
                                                       afwGeom.Extent2I(ccdInfo["ampWidth"], ccdInfo["ampHeight"])))

        self.assertEqual(ccd.getSize().getMm()[0], ccdInfo["pixelSize"]*ccdInfo["trimmedWidth"])
        self.assertEqual(ccd.getSize().getMm()[1], ccdInfo["pixelSize"]*ccdInfo["trimmedHeight"])
        #
        # Test mapping pixel <--> mm
        #
        pix = afwGeom.Point2D(99.5, 203.5)            # wrt bottom left
        pos = cameraGeom.FpPoint(0.00, 1.02)             # pixel center wrt CCD center
        posll = cameraGeom.FpPoint(0.00, 1.02)           # llc of pixel wrt CCD center
        
        self.assertEqual(ccd.getPixelFromPosition(pos), pix)
        self.assertEqual(ccd.getPositionFromPixel(pix).getMm(), posll.getMm())
コード例 #11
0
def makeRaft(raftName):
    dewar = cameraGeom.Raft(cameraGeom.Id("DECam"), 1, 1)
    dewar.addDetector(afwGeom.PointI(0, 0), cameraGeom.FpPoint(0.0, 0.0),
                      cameraGeom.Orientation(0), makeCcd(raftName))
    return dewar
コード例 #12
0
def main(camera,
         sample=20,
         names=False,
         showDistortion=True,
         plot=True,
         outputFile=None):
    if plot:
        fig = plt.figure(1)
        fig.clf()
        ax = fig.add_axes((0.1, 0.1, 0.8, 0.8))
        ax.set_title('%s  Distorted CCDs' % camera.getId().getName())
    else:
        fig = None

    dist = camera.getDistortion()

    for raft in camera:
        for ccd in raft:
            ccd.setTrimmed(True)

            width, height = ccd.getAllPixels(True).getDimensions()

            corners = ((0.0, 0.0), (0.0, height), (width, height),
                       (width, 0.0), (0.0, 0.0))
            for (x0, y0), (x1, y1) in zip(corners[0:4], corners[1:5]):
                if x0 == x1 and y0 != y1:
                    yList = numpy.linspace(y0, y1, num=sample)
                    xList = [x0] * len(yList)
                elif y0 == y1 and x0 != x1:
                    xList = numpy.linspace(x0, x1, num=sample)
                    yList = [y0] * len(xList)
                else:
                    raise RuntimeError("Should never get here")

                xOriginal = []
                yOriginal = []
                xDistort = []
                yDistort = []
                for x, y in zip(xList, yList):
                    position = ccd.getPositionFromPixel(afwGeom.Point2D(
                        x, y))  # focal plane position

                    xOriginal.append(position.getMm().getX())
                    yOriginal.append(position.getMm().getY())

                    if not showDistortion:
                        continue
                    # Calculate offset (in CCD pixels) due to distortion
                    distortion = dist.distort(afwGeom.Point2D(x, y),
                                              ccd) - afwGeom.Extent2D(x, y)

                    # Calculate the distorted position
                    distorted = position + cameraGeom.FpPoint(
                        distortion) * ccd.getPixelSize()

                    xDistort.append(distorted.getMm().getX())
                    yDistort.append(distorted.getMm().getY())

                if fig:
                    ax.plot(xOriginal, yOriginal, 'k-')
                    ax.plot(xDistort, yDistort, 'r-')

            if fig:
                x, y = ccd.getPositionFromPixel(
                    afwGeom.Point2D(width / 2, height / 2)).getMm()
                cid = ccd.getId()
                if names:
                    ax.text(x,
                            y,
                            cid.getName(),
                            ha='center',
                            rotation=90 if height > width else 0,
                            fontsize="smaller")
                else:
                    ax.text(x, y, cid.getSerial(), ha='center', va='center')

    if fig:
        if camera.getId().getName() == "HSC":
            from matplotlib.patches import Circle
            cen = (0, 0)
            ax.add_patch(Circle(cen, radius=18100, color='black', alpha=0.2))
            if showDistortion:
                ax.add_patch(Circle(cen, radius=19000, color='red', alpha=0.2))

            for x, y, t in ([-1, 0, "N"], [0, 1, "W"], [1, 0,
                                                        "S"], [0, -1, "E"]):
                ax.text(19500 * x, 19500 * y, t, ha="center", va="center")

            plt.axis([-21000, 21000, -21000, 21000])

        ax.set_aspect('equal')
        if outputFile:
            plt.savefig(outputFile)
        else:
            plt.show()