Ejemplo n.º 1
0
 def __init__(self, millarr, mprint=sys.stdout.write):
     from iotbx.gui_tools.reflections import get_array_description
     data = millarr.data()
     if (isinstance(data, flex.int)):
         data = [e for e in data if e != display.inanval]
     if millarr.is_complex_array():
         data = flex.abs(millarr.data())
     data = [e for e in data if not math.isnan(e)]
     self.maxdata = max(data)
     self.mindata = min(data)
     self.maxsigmas = self.minsigmas = None
     if millarr.sigmas() is not None:
         data = millarr.sigmas()
         data = [e for e in data if not math.isnan(e)]
         self.maxsigmas = max(data)
         self.minsigmas = min(data)
     self.minmaxdata = (roundoff(self.mindata), roundoff(self.maxdata))
     self.minmaxsigs = (roundoff(self.minsigmas), roundoff(self.maxsigmas))
     self.labels = self.desc = ""
     #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
     if millarr.info():
         self.labels = millarr.info().label_string()
         self.desc = get_array_description(millarr)
     self.span = ("?", "?")
     dmin = 0.0
     dmax = 0.0
     try:
         self.span = (millarr.index_span().min(),
                      millarr.index_span().max())
         dmin = millarr.d_max_min()[1]
         dmax = millarr.d_max_min()[0]
     except Exception, e:
         mprint(to_str(e))
Ejemplo n.º 2
0
 def __init__(self, millarr):
   from iotbx.gui_tools.reflections import get_array_description
   data = millarr.data()
   if (isinstance(data, flex.int)):
     data = [e for e in data if e!= display.inanval]
   if millarr.is_complex_array():
     data = flex.abs(millarr.data())
   self.maxdata =max( data )
   self.mindata =min( data )
   self.maxsigmas = self.minsigmas = display.nanval
   if millarr.sigmas() is not None:
     data = millarr.sigmas()
     self.maxsigmas =max( data )
     self.minsigmas =min( data )
     self.minmaxstr = "MinMaxValues:[%s; %s], MinMaxSigmaValues:[%s; %s]" \
       %(roundoff(self.mindata), roundoff(self.maxdata), \
           roundoff(self.minsigmas), roundoff(self.maxsigmas))
   else:
     self.minmaxstr = "MinMaxValues:[%s; %s]" %(roundoff(self.mindata), roundoff(self.maxdata))
   self.labels = self.desc = ""
   if millarr.info():
     self.labels = millarr.info().label_string()
     self.desc = get_array_description(millarr)
   self.span = "HKLs: %s to %s" % \
     ( millarr.index_span().min(), millarr.index_span().max())
   self.infostr = "%s (%s), %s %s, %s, d_min: %s" % \
     (self.labels, self.desc, millarr.size(), self.span, self.minmaxstr, roundoff(millarr.d_min()))
Ejemplo n.º 3
0
class ArrayInfo:
    def __init__(self, millarr, mprint=sys.stdout.write):
        from iotbx.gui_tools.reflections import get_array_description
        data = millarr.data()
        if (isinstance(data, flex.int)):
            data = [e for e in data if e != display.inanval]
        if millarr.is_complex_array():
            data = flex.abs(millarr.data())
        data = [e for e in data if not math.isnan(e)]
        self.maxdata = max(data)
        self.mindata = min(data)
        self.maxsigmas = self.minsigmas = None
        if millarr.sigmas() is not None:
            data = millarr.sigmas()
            data = [e for e in data if not math.isnan(e)]
            self.maxsigmas = max(data)
            self.minsigmas = min(data)
        self.minmaxdata = (roundoff(self.mindata), roundoff(self.maxdata))
        self.minmaxsigs = (roundoff(self.minsigmas), roundoff(self.maxsigmas))
        self.labels = self.desc = ""
        #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
        if millarr.info():
            self.labels = millarr.info().label_string()
            self.desc = get_array_description(millarr)
        self.span = ("?", "?")
        dmin = 0.0
        dmax = 0.0
        try:
            self.span = (millarr.index_span().min(),
                         millarr.index_span().max())
            dmin = millarr.d_max_min()[1]
            dmax = millarr.d_max_min()[0]
        except Exception, e:
            mprint(to_str(e))
        issymunique = millarr.is_unique_set_under_symmetry()
        self.infotpl = (self.labels, self.desc, millarr.indices().size(),
                        self.span, self.minmaxdata, self.minmaxsigs,
                        (roundoff(dmin), roundoff(dmax)), issymunique)
        self.infostr = "%s (%s), %s HKLs: %s, MinMax: %s, MinMaxSigs: %s, d_minmax: %s, SymUnique: %d" % self.infotpl
Ejemplo n.º 4
0
  def tabulate_arrays(self, datalabels):
    if len(self.origarrays) == 0: # if not an mtz file then split columns
      # SupersetMillerArrays may not be necessary if file formats except for cif and mtz can't store multiple data columns
      #self.viewer.SupersetMillerArrays()
      self.origarrays["HKLs"] = self.viewer.proc_arrays[0].indices()
      for arr in self.viewer.proc_arrays:
        if arr.is_complex_array():
          ampls, phases = self.viewer.Complex2AmplitudesPhases(arr.data())
          cmplxlst = [ "%.4f + %.4f * i"%(e.real, e.imag)
                        if not cmath.isnan(e) else display.nanval for e in arr.data() ]
          self.origarrays[arr.info().label_string()] = cmplxlst
          self.origarrays[arr.info().labels[0]] = list(ampls)
          self.origarrays[arr.info().labels[-1]] = list(phases)
        elif arr.is_hendrickson_lattman_array():
          A,B,C,D = arr.data().as_abcd()
          HLlst = [ "%.4f, %.4f, %.4f, %.4f"%(e[0], e[1], e[2], e[3]) for e in arr.data() ]
          self.origarrays[arr.info().label_string()] = HLlst
          self.origarrays[arr.info().labels[0]] = list(A)
          self.origarrays[arr.info().labels[1]] = list(B)
          self.origarrays[arr.info().labels[2]] = list(C)
          self.origarrays[arr.info().labels[3]] = list(D)
        elif arr.sigmas() is not None:
          labels = arr.info().labels
          # Labels could be something like ['I(+)', 'SIGI(+)', 'I(-)', 'SIGI(-)'].
          # So group datalabels and sigmalabels separately assuming that sigma column contain the three letters "sig"
          datalabel = ",".join([ e for e in labels if "sig" not in e.lower()])
          sigmalabel = ",".join([ e for e in labels if "sig" in e.lower()])
          self.origarrays[datalabel] = list(arr.data())
          self.origarrays[sigmalabel] = list(arr.sigmas())
        elif arr.is_integer_array():
          list_with_nans = [ e if not e==display.inanval else display.nanval for e in arr.data() ]
          if self.viewer.array_infotpls[id][0] == 'FreeR_flag': # want True or False back
            list_with_nans = [ 1==e if not cmath.isnan(e) else display.nanval for e in list_with_nans ]
          self.origarrays[arr.info().label_string()] = list_with_nans
        else:
          self.origarrays[arr.info().label_string()] = list(arr.data())

    labels = eval(datalabels)
    indices = self.origarrays["HKLs"]
    dres = self.procarrays[0].unit_cell().d( indices)
    dreslst = [("d_res", roundoff(list(dres)),3)]
    hkls = list(indices)
    hkllst = [ ("H", [e[0] for e in hkls] ), ("K", [e[1] for e in hkls] ), ("L", [e[2] for e in hkls] )]
    datalst = []
    for label in labels:
      datalst.append( (label, list(self.origarrays[label])))
    self.idx_data = hkllst + dreslst + datalst
    self.mprint("Sending table data...", verbose=0)
    mydict = { "tabulate_miller_array": self.idx_data }
    self.params.tabulate_miller_array_ids = "[]" # to allow reopening a closed window again
    self.SendInfoToGUI(mydict)
Ejemplo n.º 5
0
 def list_vectors(self):
   self.viewer.all_vectors = self.viewer.rotation_operators[:]
   if self.tncsvec is not None:
     uc = self.viewer.miller_array.unit_cell()
     # TNCS vector is specified in realspace fractional coordinates. Convert it to cartesian
     cartvec = list( self.tncsvec * matrix.sqr(uc.orthogonalization_matrix()) )
     ln = len(self.viewer.all_vectors)
     self.viewer.all_vectors.append( (ln, "TNCS", 0, cartvec, "", "", str(roundoff(self.tncsvec, 5)) ) )
   self.viewer.all_vectors = self.viewer.all_vectors + self.uservectors
   for (opnr, label, order, cartvec, hkl_op, hkl, abc) in self.viewer.all_vectors:
     # avoid onMessage-DrawVector in HKLJavaScripts.js misinterpreting the commas in strings like "-x,z+y,-y"
     name = label + hkl_op.replace(",", "_")
     self.viewer.RemovePrimitives(name)
   self.SendInfoToGUI( { "all_vectors": self.viewer.all_vectors } )
   return self.viewer.all_vectors
Ejemplo n.º 6
0
 def add_user_vector(self):
   uc = self.viewer.miller_array.unit_cell()
   ln = len(self.viewer.all_vectors)
   label = self.params.viewer.user_label
   order = 0
   try:
     hklvec = ""
     abcvec = ""
     hklop = ""
     if self.params.viewer.add_user_vector_hkl not in [None, "", "()"]:
       hklvec = eval(self.params.viewer.add_user_vector_hkl.replace(" ",""))
       # convert into cartesian space
       cartvec = list( self.viewer.scene.renderscale*(hklvec * matrix.sqr(uc.fractionalization_matrix()).transpose()) )
     elif self.params.viewer.add_user_vector_abc not in [None, "", "()"]:
       abcvec = eval(self.params.viewer.add_user_vector_abc.replace(" ",""))
       # convert into cartesian space
       cartvec = list(abcvec * matrix.sqr(uc.orthogonalization_matrix()))
     elif self.params.viewer.add_user_vector_hkl_op not in [None, ""]:
       hklop = self.params.viewer.add_user_vector_hkl_op.replace(" ","")
       rt = sgtbx.rt_mx(symbol=hklop, r_den=12, t_den=144)
       rt.r().as_double()
       self.viewer.symops.append( rt ) #
       (cartvec, a, label, order) = self.viewer.GetVectorAndAngleFromRotationMx( rt.r() )
       if label:
         label = "%s-fold_%s" %(str(int(roundoff(2*math.pi/a, 0))), self.params.viewer.user_label)
         self.mprint("Rotation axis, %s, added" %label)
     if (self.params.viewer.add_user_vector_hkl in [None, "", "()"] \
      and self.params.viewer.add_user_vector_abc in [None, "", "()"] \
      and self.params.viewer.add_user_vector_hkl_op) in [None, ""]:
       self.mprint("No vector was specified")
     self.uservectors.append( (ln, label, order, cartvec, hklop, str(hklvec), str(abcvec) ))
     self.list_vectors()
   except Exception as e:
     raise Sorry( str(e))
   self.params.viewer.add_user_vector_hkl_op = ""
   self.params.viewer.add_user_vector_hkl = ""
   self.params.viewer.add_user_vector_abc = ""
Ejemplo n.º 7
0
    def __init__(self, millarr, wrap_labels=0):
        from cctbx.miller import display2
        from cctbx.array_family import flex
        from scitbx import graphics_utils
        from libtbx.math_utils import roundoff
        import math
        nan = float("nan")
        self.wrap_labels = wrap_labels
        if millarr.space_group() is None:
            self.spginf = "?"
        else:
            self.spginf = millarr.space_group_info().symbol_and_number()
        if millarr.unit_cell() is None:
            self.ucell = (nan, nan, nan, nan, nan, nan)
        else:
            self.ucell = millarr.unit_cell().parameters()
        self.ucellinf = "({:.6g}Å, {:.6g}Å, {:.6g}Å, {:.6g}°, {:.6g}°, {:.6g}°)".format(
            *self.ucell)
        data = millarr.deep_copy().data()
        self.maxdata = self.mindata = self.maxsigmas = self.minsigmas = nan
        self.minmaxdata = (nan, nan)
        self.minmaxsigs = (nan, nan)
        self.data_sigdata_max = nan
        self.data_sigdata = nan
        self.desc = ""
        self.arrsize = data.size()
        if not isinstance(data, flex.std_string):
            if isinstance(data, flex.hendrickson_lattman):
                data = graphics_utils.NoNansHL(data)
                # estimate minmax values of HL coefficients as a simple sum
                if self.arrsize:
                    self.maxdata = max(
                        [e[0] + e[1] + e[2] + e[3] for e in data])
                    self.mindata = min(
                        [e[0] + e[1] + e[2] + e[3] for e in data])
                    self.arrsize = len([
                        42 for e in millarr.data()
                        if not math.isnan(e[0] + e[1] + e[2] + e[3])
                    ])
            elif isinstance(data, flex.vec3_double) or isinstance(
                    data, flex.vec2_double):
                # XDS produces 2D or 3D arrays in some of its files
                if self.arrsize:
                    self.maxdata = max([max(e) for e in data])
                    self.mindata = min([min(e) for e in data])
            else:
                # Get a list of bools with True whenever there is a nan
                selection = ~graphics_utils.IsNans(
                    flex.abs(millarr.data()).as_double())
                # count elements that are not nan values
                self.arrsize = millarr.data().select(selection).size()
                if (isinstance(data, flex.int)):
                    data = flex.double(
                        [e for e in data if e != display2.inanval])
                if millarr.is_complex_array():
                    data = flex.abs(data)
                i = 0
                while math.isnan(data[i]):
                    i += 1  # go on until we find a data[i] that isn't NaN
                data = graphics_utils.NoNansArray(
                    data, data[i])  # assuming data[0] isn't NaN
                self.maxdata = flex.max(data)
                self.mindata = flex.min(data)
            if millarr.sigmas() is not None:
                data = millarr.sigmas().deep_copy()
                i = 0
                while math.isnan(data[i]):
                    i += 1  # go on until we find a data[i] that isn't NaN
                data = graphics_utils.NoNansArray(data, data[i])
                self.maxsigmas = flex.max(data)
                self.minsigmas = flex.min(data)
                # Inspired by Diederichs doi:10.1107/S0907444910014836 I/SigI_asymptotic
                data_over_sigdata = millarr.data() / millarr.sigmas()
                self.data_sigdata = flex.sum(data_over_sigdata) / len(
                    data_over_sigdata)
                self.data_sigdata_max = flex.max(data_over_sigdata)
            self.minmaxdata = (self.mindata, self.maxdata)
            self.minmaxsigs = (self.minsigmas, self.maxsigmas)
        self.labels = self.desc = self.wavelength = ""
        if millarr.info():
            self.labels = millarr.info().labels
            self.desc = get_array_description(millarr)
            self.wavelength = "{:.6g}".format(
                millarr.info().wavelength) if millarr.info(
                ).wavelength is not None else float("nan")
        self.span = "(?,?,?), (?,?,?)"
        self.dmin = nan
        self.dmax = nan
        if millarr.unit_cell() is not None:
            self.span = str(millarr.index_span().min()) + ", " + str(
                millarr.index_span().max())
            self.dmin = millarr.d_max_min()[1]
            self.dmax = millarr.d_max_min()[0]
        self.dminmax = roundoff((self.dmin, self.dmax))
        self.issymunique = "?"
        self.isanomalous = "?"
        self.n_sys_abs = 0
        self.n_bijvoet = self.n_singletons = 0
        self.ano_mean_diff = nan
        self.ano_completeness = nan
        self.data_compl_infty = nan
        self.data_completeness = nan
        self.n_centric = nan
        # computations below done as in cctbx.miller.set.show_comprehensive_summary()
        if self.spginf != "?":
            self.issymunique = millarr.is_unique_set_under_symmetry()
            self.isanomalous = millarr.anomalous_flag()
            sys_absent_flags = millarr.sys_absent_flags().data()
            self.n_sys_abs = sys_absent_flags.count(True)
            if (self.n_sys_abs != 0):
                millarr = millarr.select(selection=~sys_absent_flags)
            self.n_centric = millarr.centric_flags().data().count(True)
        if not math.isnan(self.ucell[0]):
            if (self.spginf != "?" and millarr.indices().size() > 0
                    and self.issymunique):
                millarr.setup_binner(n_bins=1)
                completeness_d_max_d_min = millarr.completeness(
                    use_binning=True)
                binner = completeness_d_max_d_min.binner
                assert binner.counts_given()[0] == 0
                assert binner.counts_given()[2] == 0
                n_obs = binner.counts_given()[1]
                n_complete = binner.counts_complete()[1]
                if (n_complete != 0 and self.dmax != self.dmin):
                    self.data_completeness = n_obs / n_complete
                n_complete += binner.counts_complete()[0]
                if (n_complete != 0):
                    self.data_compl_infty = n_obs / n_complete
                if (self.isanomalous) and (millarr.is_xray_intensity_array() or
                                           millarr.is_xray_amplitude_array()):
                    self.ano_completeness = millarr.anomalous_completeness()
        if (self.spginf != "?" and self.isanomalous and self.issymunique):
            asu, matches = millarr.match_bijvoet_mates()
            self.n_bijvoet = matches.pairs().size()
            self.n_singletons = matches.n_singles() - self.n_centric
            if millarr.is_real_array():
                self.ano_mean_diff = millarr.anomalous_signal()
        # break long label into list of shorter strings
        self.labelstr = ",".join(self.labels)
        if self.wrap_labels > 0:
            tlabels = textwrap.wrap(self.labelstr, width=self.wrap_labels)
            nlabl = len(tlabels)
            self.labelsformat = "{0[0]:>%d} " % (1 + self.wrap_labels)
            if len(tlabels) > 1:
                for i in range((len(tlabels) - 1)):
                    self.labelsformat += "\n{0[%d]:>%d} " % (
                        i + 1, self.wrap_labels + 1)
            blanks = self.wrap_labels - 5
        else:
            self.labelsformat = "{:>16} "
            if len(self.labelstr) > 15:
                self.labelsformat = "{}\n                 "
            blanks = 10

        self.info_format_dict = {
            # the keys here must be verbatim copies of names of phil attributes in arrayinfo_phil_str below
            "labels": (" %s" % self.caption_dict["labels"][0] + " " * blanks,
                       self.labelstr, "{}", self.labelsformat),
            "description":
            ("       %s      " % self.caption_dict["description"][0],
             self.desc, "{}", "{:>16} "),
            "wavelength": ("   %s   " % self.caption_dict["wavelength"][0],
                           self.wavelength, "{}", "{:>8} "),
            "n_reflections": ("  %s  " % self.caption_dict["n_reflections"][0],
                              self.arrsize, "{}", "{:>8} "),
            "span": (" " * 15 + self.caption_dict["span"][0] + " " * 14,
                     self.span, "{}", "{:>32} "),
            "minmax_data":
            ("     %s       " % self.caption_dict["minmax_data"][0],
             self.minmaxdata, "{0[0]:.6}, {0[1]:.6}",
             "{0[0]:>11.5}, {0[1]:>11.5}"),
            "minmax_sigmas":
            ("     %s     " % self.caption_dict["minmax_sigmas"][0],
             self.minmaxsigs, "{0[0]:.6}, {0[1]:.6}",
             "{0[0]:>11.5}, {0[1]:>11.5}"),
            "data_sigdata": (" %s" % self.caption_dict["data_sigdata"][0],
                             self.data_sigdata, "{:.4g}", "{:>9.4g} "),
            "data_sigdata_max":
            ("%s" % self.caption_dict["data_sigdata_max"][0],
             self.data_sigdata_max, "{:.4g}", "{:>11.4g} "),
            "d_minmax": ("  %s   " % self.caption_dict["d_minmax"][0],
                         self.dminmax, "{0[0]:.6}, {0[1]:.6}",
                         "{0[0]:>8.5}, {0[1]:>8.5}"),
            "unit_cell":
            ("     %s      " % self.caption_dict["unit_cell"][0], self.ucell,
             "{0[0]:>7.5g},{0[1]:>7.5g},{0[2]:>7.5g},{0[3]:>7.5g},{0[4]:>7.5g},{0[5]:>7.5g}",
             "{0[0]:>7.5g},{0[1]:>7.5g},{0[2]:>7.5g},{0[3]:>7.5g},{0[4]:>7.5g},{0[5]:>7.5g} "
             ),
            "space_group":
            ("   %s      " % self.caption_dict["space_group"][0], self.spginf,
             "{}", "{:>19} "),
            "n_centrics": ("%s" % self.caption_dict["n_centrics"][0],
                           self.n_centric, "{}", "{:>8} "),
            "n_sys_abs": ("%s" % self.caption_dict["n_sys_abs"][0],
                          self.n_sys_abs, "{}", "{:>9} "),
            "data_completeness":
            ("%s" % self.caption_dict["data_completeness"][0],
             self.data_completeness, "{:.5g}", "{:>10.5g} "),
            "data_compl_infty":
            ("%s" % self.caption_dict["data_compl_infty"][0],
             self.data_compl_infty, "{:.5g}", "{:>9.5g} "),
            "is_anomalous": ("%s" % self.caption_dict["is_anomalous"][0],
                             str(self.isanomalous), "{}", "{:>8} "),
            "is_symmetry_unique":
            ("%s" % self.caption_dict["is_symmetry_unique"][0],
             str(self.issymunique), "{}", "{:>8} "),
            "ano_completeness":
            ("%s" % self.caption_dict["ano_completeness"][0],
             self.ano_completeness, "{:.5g}", "{:>11.5g} "),
            "ano_mean_diff": ("%s" % self.caption_dict["ano_mean_diff"][0],
                              self.ano_mean_diff, "{:.5g}", "{:>8.5g} "),
            "n_bijvoet": ("%s" % self.caption_dict["n_bijvoet"][0],
                          self.n_bijvoet, "{}", "{:>8} "),
            "n_singletons": ("%s" % self.caption_dict["n_singletons"][0],
                             self.n_singletons, "{}", "{:>10} "),
        }
Ejemplo n.º 8
0
    def DrawNGLJavaScript(self):
        if self.miller_array is None:
            self.mprint("A miller array must be selected for drawing")
            return
        self.mprint("Composing NGL JavaScript...")
        h_axis = self.scene.axes[0]
        k_axis = self.scene.axes[1]
        l_axis = self.scene.axes[2]
        nrefls = self.scene.points.size()

        Hstararrowstart = roundoff(
            [-h_axis[0] * 100, -h_axis[1] * 100, -h_axis[2] * 100])
        Hstararrowend = roundoff(
            [h_axis[0] * 100, h_axis[1] * 100, h_axis[2] * 100])
        Hstararrowtxt = roundoff(
            [h_axis[0] * 102, h_axis[1] * 102, h_axis[2] * 102])
        Kstararrowstart = roundoff(
            [-k_axis[0] * 100, -k_axis[1] * 100, -k_axis[2] * 100])
        Kstararrowend = roundoff(
            [k_axis[0] * 100, k_axis[1] * 100, k_axis[2] * 100])
        Kstararrowtxt = roundoff(
            [k_axis[0] * 102, k_axis[1] * 102, k_axis[2] * 102])
        Lstararrowstart = roundoff(
            [-l_axis[0] * 100, -l_axis[1] * 100, -l_axis[2] * 100])
        Lstararrowend = roundoff(
            [l_axis[0] * 100, l_axis[1] * 100, l_axis[2] * 100])
        Lstararrowtxt = roundoff(
            [l_axis[0] * 102, l_axis[1] * 102, l_axis[2] * 102])
        # make arrow font size roughly proportional to radius of highest resolution shell
        fontsize = str(1.0 + roundoff(
            math.pow(max(self.miller_array.index_span().max()), 1.0 / 3.0)))

        arrowstr = """
    // xyz arrows
    shape.addSphere( [0,0,0] , [ 1, 1, 1 ], 0.3, 'Origo');
    //blue-x
    shape.addArrow( %s, %s , [ 0, 0, 1 ], 0.1);
    //green-y
    shape.addArrow( %s, %s , [ 0, 1, 0 ], 0.1);
    //red-z
    shape.addArrow( %s, %s , [ 1, 0, 0 ], 0.1);

    shape.addText( %s, [ 0, 0, 1 ], %s, 'h');
    shape.addText( %s, [ 0, 1, 0 ], %s, 'k');
    shape.addText( %s, [ 1, 0, 0 ], %s, 'l');
    """ % (str(Hstararrowstart), str(Hstararrowend), str(Kstararrowstart),
           str(Kstararrowend), str(Lstararrowstart), str(Lstararrowend),
           Hstararrowtxt, fontsize, Kstararrowtxt, fontsize, Lstararrowtxt,
           fontsize)

        # Make colour gradient array used for drawing a bar of colours next to associated values on the rendered html
        mincolourscalar = self.othermindata[self.icolourcol]
        maxcolourscalar = self.othermaxdata[self.icolourcol]
        if self.settings.sigma_color:
            mincolourscalar = self.otherminsigmas[self.icolourcol]
            maxcolourscalar = self.othermaxsigmas[self.icolourcol]
        span = maxcolourscalar - mincolourscalar
        ln = 60
        incr = span / ln
        colourgradarrays = []
        val = mincolourscalar
        colourscalararray = flex.double()
        colourscalararray.append(val)
        for j, sc in enumerate(range(ln)):
            val += incr
            colourscalararray.append(val)
        if self.otherscenes[self.icolourcol].miller_array.is_complex_array():
            # When displaying phases from map coefficients together with fom values
            # compute colour map chart as a function of fom and phase values (x,y axis)
            incr = 360.0 / ln
            val = 0.0
            colourscalararray = flex.double()
            colourscalararray.append(val)
            for j in enumerate(range(ln)):
                val += incr
                colourscalararray.append(val)

            fomarrays = []
            if self.otherscenes[self.icolourcol].isUsingFOMs():
                fomln = 50
                fom = 1.0
                fomdecr = 1.0 / (fomln - 1.0)
                # make fomln fom arrays of size len(colourscalararray) when calling colour_by_phi_FOM
                for j in range(fomln):
                    fomarrays.append(flex.double(len(colourscalararray), fom))
                    fom -= fomdecr
                for j in range(fomln):
                    colourgradarrays.append(
                        graphics_utils.colour_by_phi_FOM(
                            colourscalararray *
                            (math.pi / 180.0), fomarrays[j]) * 255.0)
            else:
                fomln = 1
                fomarrays = [1.0]
                colourgradarrays.append(
                    graphics_utils.colour_by_phi_FOM(colourscalararray *
                                                     (math.pi / 180.0)) *
                    255.0)
        else:
            fomln = 1
            fomarrays = [1.0]
            colourgradarrays.append(
                graphics_utils.color_by_property(
                    properties=flex.double(colourscalararray),
                    selection=flex.bool(len(colourscalararray), True),
                    color_all=False,
                    gradient_type=self.settings.color_scheme) * 255.0)

        colors = self.otherscenes[self.icolourcol].colors
        radii = self.otherscenes[self.iradiicol].radii
        points = self.scene.points
        hkls = self.scene.indices
        dres = self.scene.dres
        colstr = self.scene.miller_array.info().label_string()
        data = self.scene.data
        colourlabel = self.otherscenes[self.icolourcol].colourlabel
        fomlabel = self.otherscenes[self.icolourcol].fomlabel
        #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
        assert (colors.size() == radii.size() == nrefls)
        colours = []
        positions = []
        radii2 = []
        spbufttips = []

        self.workingbinvals = []
        if not self.binarray == "Resolution":
            ibinarray = int(self.binarray)
            self.workingbinvals = [
                self.othermindata[ibinarray] - 0.1,
                self.othermaxdata[ibinarray] + 0.1
            ]
            self.workingbinvals.extend(self.binvals)
            self.workingbinvals.sort()
            if self.workingbinvals[0] < 0.0:
                self.workingbinvals.append(0.0)
                self.workingbinvals.sort()
            bindata = self.otherscenes[ibinarray].data
            if self.otherscenes[ibinarray].work_array.is_complex_array():
                bindata = self.otherscenes[ibinarray].ampl
        else:
            self.workingbinvals = self.binvals
            colstr = "dres"
            bindata = 1.0 / dres
        self.nbin = len(self.workingbinvals)

        for ibin in range(self.nbin):
            colours.append([])  # colours and positions are 3 x size of data()
            positions.append([])
            radii2.append([])
            spbufttips.append([])

        def data2bin(d):
            for ibin, binval in enumerate(self.workingbinvals):
                if (ibin + 1) == self.nbin:
                    return ibin
                if d > binval and d <= self.workingbinvals[ibin + 1]:
                    return ibin
            #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
            raise Sorry("Should never get here")

        #import code, traceback; code.interact(local=locals(), banner="".join( traceback.format_stack(limit=10) ) )
        for i, hklstars in enumerate(points):
            # bin currently displayed data according to the values of another miller array
            ibin = data2bin(bindata[i])
            spbufttip = 'H,K,L: %s, %s, %s' % (hkls[i][0], hkls[i][1],
                                               hkls[i][2])
            spbufttip += '\ndres: %s ' % str(roundoff(dres[i]))
            spbufttip += '\' + AA + \''
            for j, otherscene in enumerate(self.otherscenes):
                ocolstr = self.proc_arrays[j].info().label_string()
                odata = otherscene.data
                od = ""
                if self.proc_arrays[j].is_complex_array():
                    if not math.isnan(otherscene.foms[i]):
                        od = str(roundoff(otherscene.ampl[i])) + ", " + str(roundoff(otherscene.phases[i])  ) + \
                          "\' + DGR + \'" +  ", " + str(roundoff(otherscene.foms[i])  )
                    else:
                        od = str(roundoff(otherscene.ampl[i])) + ", " + str(roundoff(otherscene.phases[i])  ) + \
                          "\' + DGR + \'"
                elif self.proc_arrays[j].sigmas() is not None:
                    od = str(roundoff(odata[i])) + ", " + str(
                        roundoff(otherscene.sigmas[i]))
                else:
                    od = str(roundoff(odata[i]))
                if not (math.isnan(abs(odata[i]))
                        or odata[i] == display.inanval):
                    spbufttip += "\n%s: %s" % (ocolstr, od)
            positions[ibin].extend(roundoff(list(hklstars)))
            colours[ibin].extend(roundoff(list(colors[i]), 2))
            radii2[ibin].append(roundoff(radii[i], 2))
            spbufttips[ibin].append(spbufttip)

        spherebufferstr = """
  ttips = new Array(%d)
  positions = new Array(%d)
  colours = new Array(%d)
  radii = new Array(%d)
  spherebufs = new Array(%d)
    """ % (self.nbin, self.nbin, self.nbin, self.nbin, self.nbin)

        negativeradiistr = ""
        cntbin = 0
        self.binstrs = []
        for ibin in range(self.nbin):
            mstr = ""
            nreflsinbin = len(radii2[ibin])
            if (ibin + 1) < self.nbin and nreflsinbin > 0:
                bin1 = self.workingbinvals[ibin]
                bin2 = self.workingbinvals[ibin + 1]
                if colstr == "dres":
                    bin1 = 1.0 / self.workingbinvals[ibin]
                    bin2 = 1.0 / self.workingbinvals[ibin + 1]
                mstr= "bin:%d, %d reflections with %s in ]%2.2f; %2.2f]" %(cntbin, nreflsinbin, \
                        colstr, bin1, bin2)
                self.binstrs.append(mstr)
                self.mprint(mstr, verbose=True)
                spherebufferstr += """
  // %s
  ttips[%d] = %s
  positions[%d] = new Float32Array( %s )
  colours[%d] = new Float32Array( %s )
  radii[%d] = new Float32Array( %s )
  spherebufs[%d] = new NGL.SphereBuffer({
    position: positions[%d],
    color: colours[%d],
    radius: radii[%d],
    picking: ttips[%d],
  })
  //}, { disableImpostor: true }) // if true allows wireframe spheres but does not allow resizing spheres

  shape.addBuffer(spherebufs[%d])
      """ %(mstr, cntbin, str(spbufttips[ibin]).replace('\"', '\''), \
                 cntbin, str(positions[ibin]), cntbin, str(colours[ibin]), \
                 cntbin, str(radii2[ibin]), cntbin, cntbin, cntbin, cntbin, cntbin, cntbin )

                if self.workingbinvals[ibin] < 0.0:
                    negativeradiistr += "spherebufs[%d].setParameters({metalness: 1})\n" % cntbin
                cntbin += 1

        spherebufferstr += """
// create tooltip element and add to the viewer canvas
  tooltip = document.createElement("div");
  Object.assign(tooltip.style, {
    display: "none",
    position: "absolute",
    zIndex: 10,
    pointerEvents: "none",
    backgroundColor: "rgba(255, 255, 255, 0.75)",
    color: "black",
    padding: "0.1em",
    fontFamily: "sans-serif"
  });

  stage.viewer.container.appendChild(tooltip);
  // listen to `hovered` signal to move tooltip around and change its text
  stage.signals.hovered.add(function (pickingProxy) {
    if (pickingProxy && (Object.prototype.toString.call(pickingProxy.picker) === '[object Array]'  )){
      var sphere = pickingProxy.sphere;
      var cp = pickingProxy.canvasPosition;
      tooltip.innerText = pickingProxy.picker[pickingProxy.pid];
      tooltip.style.bottom = cp.y + 7 + "px";
      tooltip.style.left = cp.x + 8 + "px";
      tooltip.style.fontSize = "smaller";
      tooltip.style.display = "block";
    }else{
      tooltip.style.display = "none";
    }
  });

  stage.signals.clicked.add(function (pickingProxy) {
  if (pickingProxy && (Object.prototype.toString.call(pickingProxy.picker) === '[object Array]'  )){
    var innerText = pickingProxy.picker[pickingProxy.pid];
    mysocket.send( innerText);
  }
  });

    """
        colourgradstrs = "colourgradvalarray = new Array(%s)\n" % fomln
        # if displaying phases from map coefficients together with fom values then
        for g, colourgradarray in enumerate(colourgradarrays):
            self.colourgradientvalues = []
            for j, e in enumerate(colourgradarray):
                self.colourgradientvalues.append([colourscalararray[j], e])
            self.colourgradientvalues = roundoff(self.colourgradientvalues)

            fom = fomarrays[g]
            colourgradstr = []
            for j, val in enumerate(self.colourgradientvalues):
                vstr = ""
                alpha = 1.0
                gradval = "rgba(%s, %s, %s, %s)" % (val[1][0], val[1][1],
                                                    val[1][2], alpha)
                if j % 10 == 0 or j == len(self.colourgradientvalues) - 1:
                    vstr = str(roundoff(val[0], 2))
                colourgradstr.append([vstr, gradval])

            colourgradstrs += "  colourgradvalarray[%s] = %s\n" % (
                g, str(colourgradstr))

        #negativeradiistr = ""
        #for ibin in range(self.nbin):
        #  if self.workingbinvals[ibin] < 0.0:
        #    negativeradiistr += "spherebufs[%d].setParameters({metalness: 1})\n" %ibin



        self.NGLscriptstr = """
// Microsoft Edge users follow instructions on
// https://stackoverflow.com/questions/31772564/websocket-to-localhost-not-working-on-microsoft-edge
// to enable websocket connection

var pagename = location.pathname.substring(1);
var mysocket = new WebSocket('ws://127.0.0.1:7894/');

mysocket.onopen = function (e) {
  mysocket.send('%s now connected via websocket to ' + pagename + '\\n');
};

mysocket.onclose = function (e) {
  mysocket.send('%s now disconnecting from websocket ' + pagename + '\\n');
};

// Log errors to debugger of your browser
mysocket.onerror = function (error) {
  console.log('WebSocket Error ' + error);
};


window.addEventListener( 'resize', function( event ){
    stage.handleResize();
}, false );




var stage;
var shape;
var shapeComp;
var repr;
var AA = String.fromCharCode(197); // short for angstrom
var DGR = String.fromCharCode(176); // short for degree symbol

function createElement (name, properties, style) {
// utility function used in for loop over colourgradvalarray
  var el = document.createElement(name)
  Object.assign(el, properties)
  Object.assign(el.style, style)
  Object.assign(el.style, {
      display: "block",
      position: "absolute",
      color: "black",
      fontFamily: "sans-serif",
      fontSize: "smaller",
    }
  )
  return el
}


function addElement (el) {
// utility function used in for loop over colourgradvalarray
  Object.assign(el.style, {
    position: "absolute",
    zIndex: 10
  })
  stage.viewer.container.appendChild(el)
}


function addDivBox(txt, t, l, w, h, bgcolour='rgba(255.0, 255.0, 255.0, 0.0)') {
  divbox = createElement("div",
  {
    innerText: txt
  },
  {
    backgroundColor: bgcolour,
    color:  'rgba(0.0, 0.0, 0.0, 1.0)',
    top: t.toString() + "px",
    left: l.toString() + "px",
    width: w.toString() + "px",
    height: h.toString() + "px",
  }
  );
  addElement(divbox)
}


var hklscene = function () {
  shape = new NGL.Shape('shape');
  stage = new NGL.Stage('viewport', { backgroundColor: "grey", tooltip:false,
                                      fogNear: 100, fogFar: 100 });
  stage.setParameters( { cameraType: "%s" } );

  %s

  %s

  shapeComp = stage.addComponentFromObject(shape);
  repr = shapeComp.addRepresentation('buffer');
  shapeComp.autoView();
  repr.update()

  // if some radii are negative draw them with wireframe
  %s

  //colourgradvalarrays
  %s

  var ih = 3;
  var topr = 35
  var topr2 = 10
  var lp = 10
  var wp = 40
  var lp2 = lp + wp
  var gl = 3
  var wp2 = gl
  var fomlabelheight = 25
  if (colourgradvalarray.length === 1) {
    wp2 = 15
    fomlabelheight = 0
  }

  var wp3 = wp + colourgradvalarray.length * wp2 + 2

  totalheight = ih*colourgradvalarray[0].length + 35 + fomlabelheight
  // make a white box on top of which boxes with transparent background are placed
  // containing the colour values at regular intervals as well as label legend of
  // the displayed miller array
  addDivBox("", topr2, lp, wp3, totalheight, 'rgba(255.0, 255.0, 255.0, 1.0)');

  // print label of the miller array used for colouring
  addDivBox("%s", topr2, lp, wp, 20);

  if (colourgradvalarray.length > 1) {
    // print FOM label, 1, 0.5 and 0.0 values below colour chart
    fomtop = topr2 + totalheight - 18
    fomlp = lp + wp
    fomwp = wp3
    fomtop2 = fomtop - 13
    // print the 1 number
    addDivBox("1", fomtop2, fomlp, fomwp, 20)
    // print the 0.5 number
    leftp = fomlp + 0.48 * gl * colourgradvalarray.length
    addDivBox("0.5", fomtop2, leftp, fomwp, 20)
    // print the FOM label
    addDivBox("%s", fomtop, leftp, fomwp, 20);
    // print the 0 number
    leftp = fomlp + 0.96 * gl * colourgradvalarray.length
    addDivBox("0", fomtop2, leftp, fomwp, 20)
  }

  for (j = 0; j < colourgradvalarray[0].length; j++) {
    rgbcol = colourgradvalarray[0][j][1];
    val = colourgradvalarray[0][j][0]
    topv = j*ih + topr
    toptxt = topv - 5
    // print value of miller array if present in colourgradvalarray[0][j][0]
    addDivBox(val, toptxt, lp, wp, ih);
  }

  // draw the colour gradient
  for (g = 0; g < colourgradvalarray.length; g++) {
    leftp = g*gl + lp + wp

    // if FOM values are supplied draw colour gradients with decreasing
    // saturation values as stored in the colourgradvalarray[g] arrays
    for (j = 0; j < colourgradvalarray[g].length; j++) {
      rgbcol = colourgradvalarray[g][j][1];
      val = colourgradvalarray[g][j][0]
      topv = j*ih + topr
      addDivBox("", topv, leftp, wp2, ih, rgbcol);
    }
  }

}

document.addEventListener('DOMContentLoaded', function() { hklscene() }, false );


mysocket.onmessage = function (e) {
  //alert('Server: ' + e.data);
  var c
  var alpha
  var si
  mysocket.send('got ' + e.data ); // tell server what it sent us
  try {
    val = e.data.split(",")

    if (val[0] === "alpha") {
      ibin = parseInt(val[1])
      alpha = parseFloat(val[2])
      spherebufs[ibin].setParameters({opacity: alpha})
      stage.viewer.requestRender()
    }

    if (val[0] === "colour") {
      ibin = parseInt(val[1])
      si =  parseInt(val[2])
      colours[ibin][3*si] = parseFloat(val[3])
      colours[ibin][3*si+1] = parseFloat(val[4])
      colours[ibin][3*si+2] = parseFloat(val[5])
      spherebufs[ibin].setAttributes({ color: colours[ibin] })
      stage.viewer.requestRender()
    }

    if (val[0] === "Redraw") {
      stage.viewer.requestRender()
    }

    if (val[0] === "ReOrient") {
      mysocket.send( 'Reorienting ' + pagename );
      sm = new Float32Array(16);
      for (j=0; j<16; j++)
        sm[j] = parseFloat(val[j + 2]) // first 2 are "ReOrient", "NGL\\n"

      var m = new NGL.Matrix4();
      m.fromArray(sm);
      stage.viewerControls.orient(m);
      stage.viewer.requestRender();
    }

    if (val[0] === "Reload") {
    // refresh browser with the javascript file
      cvorient = stage.viewerControls.getOrientation().elements
      msg = String(cvorient)
      mysocket.send('Current vieworientation:\\n, ' + msg );

      mysocket.send( 'Refreshing ' + pagename );
      window.location.reload(true);
    }

    if (val[0] === "Testing") {
      // test something new
      mysocket.send( 'Testing something new ' + pagename );
      var newradii = radii[0].map(function(element) {
        return element*1.5;
      });

      spherebufs[0].setAttributes({
          radius: newradii
        })
      stage.viewer.requestRender()
    }

  }
  catch(err) {
    mysocket.send('error: ' + err );
  }
};




    """ % (self.__module__, self.__module__, self.cameratype, arrowstr, spherebufferstr, \
                negativeradiistr, colourgradstrs, colourlabel, fomlabel)
        if self.jscriptfname:
            with open(self.jscriptfname, "w") as f:
                f.write(self.NGLscriptstr)
        self.ReloadNGL()