问:
How to send hierarchical data from firebase to google sheet
below is the hierarchy of my data present in firebase database. I want to send this data to google sheet but I am unable to do so.

Error stated by google sheet is "TypeError: Cannot read property "rytf1JLXetTIINjHNku0yAvs7su2" from undefined.
Details"

Below is the code that I am trying to retrieve data. Kindly let me know the mistake. Thank you!

```<code>function writeSheets() {
    var ss = SpreadsheetApp.openById("google-sheet-id");
    var firebaseUrl = "database-url";
    var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
    var data = base.getData('users/admin');

    Logger.log(data);
    var num = 2;
    range = ss.getRange("A" + num + ":E" + num + "");
    for (var i in data) {
        var values = [
            [
                [
                    [data][data][i] // Error occurs here
                ].email, [
                    [data][data][i] // Error occurs here
                ].userName, [
                    [data][data] // Error occurs here
                    [i]
                ].Password, [
                    [data][data][i]  // Error occurs here
                ].loginState, [
                    [data][data][i]  // Error occurs here
                ].uid
            ]
        ];
        range.setValues(values);
        num += 1;
        range = ss.getRange("A" + num + ":E" + num + "");
    }

}
</code>```

Sample Value of data

```<code>{
   rytf1JLXetTIINjHNku0yAvs7su2:
      {
        loginDetail:
            {
              uid:rytf1JLXetTIINjHNku0yAvs7su2,
              password:123456,
              loginState:true,
              userName:John,
              email:a@a.com
        }
      }
    }
</code>```

答:
From your question and comments, I thought that you want to do as follows.

You want to put the values of "email", "userName", "password", "loginState", "uid" to the Spreadsheet every row.

You want to put the value from 2nd row of the 1st sheet in the Spreadsheet.

The structure of object data is like as follows. You want to retrieve each value from the object loginDetail.

```<code>{
  "rytf1JLXetTIINjHNku0yAvs7su2": {
    "loginDetail": {
      "uid": "rytf1JLXetTIINjHNku0yAvs7su2",
      "password": 123456,
      "loginState": true,
      "userName": "John",
      "email": "a@a.com"
    }
  }
};
</code>```

If my understanding is correct, how about this modification?

Modification points:

Find loginDetail at the 2nd level, and the object of loginDetail is retrieved.

From the retrieved object loginDetail, each value of "email", "userName", "password", "loginState", "uid" is retrieved in order.

Values of "email", "userName", "password", "loginState", "uid" are put from the 2nd row of 1st sheet of the Spreadsheet.

Modified script:

```<code>function writeSheets() {
  var ss = SpreadsheetApp.openById("google-sheet-id");
  var firebaseUrl = "database-url";
  var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
  var data = base.getData('users/admin');

  Logger.log(data);

  // I modified below script.
  var keys = ["email", "userName", "password", "loginState", "uid"];
  var res = [];
  for (var i in data) {
    for (var j in data[i]) {
      if (j == "loginDetail") {
        res.push(keys.map(function(e) {return data[i][j][e]}));
      }
    }
  }
  var num = 2;
  ss.getRange("A" + num + ":E" + (res.length + num - 1)).setValues(res);
}
</code>```

Note:

If the structure of object data is changed, it is required to traverse the object. At that time, please tell me.

Reference:

getRange(a1Notation)

Body:
below is the hierarchy of my data present in firebase database. I want to send this data to google sheet but I am unable to do so.

Error stated by google sheet is "TypeError: Cannot read property "rytf1JLXetTIINjHNku0yAvs7su2" from undefined.
Details"

Below is the code that I am trying to retrieve data. Kindly let me know the mistake. Thank you!

function writeSheets() {
    var ss = SpreadsheetApp.openById("google-sheet-id");
    var firebaseUrl = "database-url";
    var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
    var data = base.getData('users/admin');

    Logger.log(data);
    var num = 2;
    range = ss.getRange("A" + num + ":E" + num + "");
    for (var i in data) {
        var values = [
            [
                [
                    [data][data][i] // Error occurs here
                ].email, [
                    [data][data][i] // Error occurs here
                ].userName, [
                    [data][data] // Error occurs here
                    [i]
                ].Password, [
                    [data][data][i]  // Error occurs here
                ].loginState, [
                    [data][data][i]  // Error occurs here
                ].uid
            ]
        ];
        range.setValues(values);
        num += 1;
        range = ss.getRange("A" + num + ":E" + num + "");
    }

}

Sample Value of data

{
   rytf1JLXetTIINjHNku0yAvs7su2:
      {
        loginDetail:
            {
              uid:rytf1JLXetTIINjHNku0yAvs7su2,
              password:123456,
              loginState:true,
              userName:John,
              email:a@a.com
        }
      }
    }

Body:
From your question and comments, I thought that you want to do as follows.

You want to put the values of "email", "userName", "password", "loginState", "uid" to the Spreadsheet every row.

You want to put the value from 2nd row of the 1st sheet in the Spreadsheet.

The structure of object data is like as follows. You want to retrieve each value from the object loginDetail.

{
  "rytf1JLXetTIINjHNku0yAvs7su2": {
    "loginDetail": {
      "uid": "rytf1JLXetTIINjHNku0yAvs7su2",
      "password": 123456,
      "loginState": true,
      "userName": "John",
      "email": "a@a.com"
    }
  }
};

If my understanding is correct, how about this modification?

Modification points:

Find loginDetail at the 2nd level, and the object of loginDetail is retrieved.

From the retrieved object loginDetail, each value of "email", "userName", "password", "loginState", "uid" is retrieved in order.

Values of "email", "userName", "password", "loginState", "uid" are put from the 2nd row of 1st sheet of the Spreadsheet.

Modified script:

function writeSheets() {
  var ss = SpreadsheetApp.openById("google-sheet-id");
  var firebaseUrl = "database-url";
  var base = FirebaseApp.getDatabaseByUrl(firebaseUrl);
  var data = base.getData('users/admin');

  Logger.log(data);

  // I modified below script.
  var keys = ["email", "userName", "password", "loginState", "uid"];
  var res = [];
  for (var i in data) {
    for (var j in data[i]) {
      if (j == "loginDetail") {
        res.push(keys.map(function(e) {return data[i][j][e]}));
      }
    }
  }
  var num = 2;
  ss.getRange("A" + num + ":E" + (res.length + num - 1)).setValues(res);
}

Note:

If the structure of object data is changed, it is required to traverse the object. At that time, please tell me.

Reference:

getRange(a1Notation)


来源: stackexchange

问:
How to get text instead of value in ASP.NET MVC calculated field for list item?
In the following model class I am attempting to create a calculated field called FullAddress.  I am able to get it to work, but right now it displays the values and not the text for 4 different ID fields.  I would like to display the text instead of the value for the following fields:

TLRoadDirectionID

TLRoadTypeID

VendorAddUnitTypeOneID

VendorAddUnitTypeTwoID

I received some great help earlier on correctly creating FullAddress, so I know that if I were to change the fields to:

TLRoadDirection.Direction

TLRoadType.RdType

TLUnitTypeOne.UnitType

TLUnitTypeTwo.UnitType

then it works as expected, but only if all of those 4 fields contain a value and are not null or empty.  But these fields will not always have values for each record.

So, how can I modify this calculated field to show the text for each when there is text to show, and not get a null reference exception when any or all of these 4 fields are null or empty?

Below is the class (please let me know if additional information is required):

```<code>using System;
using System.ComponentModel.DataAnnotations;
using System.Web;

namespace VendorMgr.Models
{
public class Vendor
{
    [Display(Name = "Vendor ID")]
    public int ID { get; set; }

    private string _createdBy = HttpContext.Current.User.Identity.Name;
    [Required]
    [Display(Name = "Created By")]
    public string VendorCreatedBy
    {
        get
        {
            return (_createdBy == HttpContext.Current.User.Identity.Name) ? HttpContext.Current.User.Identity.Name : _createdBy;
        }
        set
        {
            _createdBy = HttpContext.Current.User.Identity.Name;
        }
    }

    private DateTime _createdOn = DateTime.Now;
    [Required]
    [Display(Name = "Date Created")]
    [DataType(DataType.DateTime)]
    public DateTime VendorCreatedDt
    {
        get
        {
            return (_createdOn == DateTime.Now) ? DateTime.Now : _createdOn;
        }
        set
        {
            _createdOn = value;
        }
    }

    [Required]
    [StringLength(128)]
    [Display(Name = "Vendor Name")]
    public string VendorName { get; set; }

    [Display(Name = "Phone Number")]
    [DataType(DataType.PhoneNumber)]
    [RegularExpression(@"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$", ErrorMessage = "Not a valid phone number")]
    public string VendorPhoneNumber { get; set; }

    [Display(Name = "Email")]
    [StringLength(255)]
    [EmailAddress(ErrorMessage = "Invalid Email Address")]
    public string VendorEmail { get; set; }

    [Display(Name = "Address Number")]
    [StringLength(10)]
    public string VendorAddNum { get; set; }

    [Display(Name = "Direction")]
    public int? TLRoadDirectionID { get; set; }

    [Display(Name = "Road Name")]
    [StringLength(128)]
    public string VendorAddName { get; set; }

    [Display(Name = "Road Type")]
    public int? TLRoadTypeID { get; set; }

    [Display(Name = "Unit Type")]
    public int? VendorAddUnitTypeOneID { get; set; }

    [Display(Name = "Unit")]
    [StringLength(20)]
    public string VendorAddUnitOne { get; set; }

    [Display(Name = "Unit Type")]
    public int? VendorAddUnitTypeTwoID { get; set; }

    [Display(Name = "Unit")]
    [StringLength(20)]
    public string VendorAddUnitTwo { get; set; }

    [Display(Name = "Full Address")]
    public string FullAddress
    {
        get
        {
            return $"{VendorAddNum} {TLRoadDirectionID} {VendorAddName} {TLRoadTypeID} {VendorAddUnitTypeOneID} {TLUnitTypeOne.UnitType} {VendorAddUnitOne} {VendorAddUnitTypeTwoID} {VendorAddUnitTwo}";
        }
    }

    [Display(Name = "City")]
    [StringLength(128)]
    public string VendorAddCity { get; set; }

    [Display(Name = "State")]
    public int? TLStateAcronymnID { get; set; }

    [Display(Name = "Zip Code")]
    [StringLength(20)]
    public string VendorZipCode { get; set; }

    [Display(Name = "Country")]
    public int? TLCountryID { get; set; }

    [Display(Name = "Active")]
    public bool VendorActive { get; set; }

    public virtual TLRoadDirection TLRoadDirection { get; set; }
    public virtual TLRoadType TLRoadType { get; set; }
    public virtual TLUnitType TLUnitTypeOne { get; set; }
    public virtual TLUnitType TLUnitTypeTwo { get; set; }
    public virtual TLStateAcronymn TLStateAcronymn { get; set; }
    public virtual TLCountry TLCountry { get; set; }
}
</code>```

}

答:
```<code>[Display(Name = "Full Address")]
public string FullAddress
{
    get
    {
        IEnumerable<string> allFields = new[] { VendorAddNum, TLRoadDirectionID, VendorAddName, TLRoadTypeID, VendorAddUnitTypeOneID, TLUnitTypeOne.UnitType, VendorAddUnitOne, VendorAddUnitTypeTwoID, VendorAddUnitTwo };
        return String.Join(" ", allFields.Where<string>(x => x != null));
    }
}
</code>```

ContentLicense: CC BY-SA 4.0

Body:
In the following model class I am attempting to create a calculated field called FullAddress.  I am able to get it to work, but right now it displays the values and not the text for 4 different ID fields.  I would like to display the text instead of the value for the following fields:

TLRoadDirectionID

TLRoadTypeID

VendorAddUnitTypeOneID

VendorAddUnitTypeTwoID

I received some great help earlier on correctly creating FullAddress, so I know that if I were to change the fields to:

TLRoadDirection.Direction

TLRoadType.RdType

TLUnitTypeOne.UnitType

TLUnitTypeTwo.UnitType

then it works as expected, but only if all of those 4 fields contain a value and are not null or empty.  But these fields will not always have values for each record.

So, how can I modify this calculated field to show the text for each when there is text to show, and not get a null reference exception when any or all of these 4 fields are null or empty?

Below is the class (please let me know if additional information is required):

using System;
using System.ComponentModel.DataAnnotations;
using System.Web;

namespace VendorMgr.Models
{
public class Vendor
{
    [Display(Name = "Vendor ID")]
    public int ID { get; set; }

    private string _createdBy = HttpContext.Current.User.Identity.Name;
    [Required]
    [Display(Name = "Created By")]
    public string VendorCreatedBy
    {
        get
        {
            return (_createdBy == HttpContext.Current.User.Identity.Name) ? HttpContext.Current.User.Identity.Name : _createdBy;
        }
        set
        {
            _createdBy = HttpContext.Current.User.Identity.Name;
        }
    }

    private DateTime _createdOn = DateTime.Now;
    [Required]
    [Display(Name = "Date Created")]
    [DataType(DataType.DateTime)]
    public DateTime VendorCreatedDt
    {
        get
        {
            return (_createdOn == DateTime.Now) ? DateTime.Now : _createdOn;
        }
        set
        {
            _createdOn = value;
        }
    }

    [Required]
    [StringLength(128)]
    [Display(Name = "Vendor Name")]
    public string VendorName { get; set; }

    [Display(Name = "Phone Number")]
    [DataType(DataType.PhoneNumber)]
    [RegularExpression(@"^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$", ErrorMessage = "Not a valid phone number")]
    public string VendorPhoneNumber { get; set; }

    [Display(Name = "Email")]
    [StringLength(255)]
    [EmailAddress(ErrorMessage = "Invalid Email Address")]
    public string VendorEmail { get; set; }

    [Display(Name = "Address Number")]
    [StringLength(10)]
    public string VendorAddNum { get; set; }

    [Display(Name = "Direction")]
    public int? TLRoadDirectionID { get; set; }

    [Display(Name = "Road Name")]
    [StringLength(128)]
    public string VendorAddName { get; set; }

    [Display(Name = "Road Type")]
    public int? TLRoadTypeID { get; set; }

    [Display(Name = "Unit Type")]
    public int? VendorAddUnitTypeOneID { get; set; }

    [Display(Name = "Unit")]
    [StringLength(20)]
    public string VendorAddUnitOne { get; set; }

    [Display(Name = "Unit Type")]
    public int? VendorAddUnitTypeTwoID { get; set; }

    [Display(Name = "Unit")]
    [StringLength(20)]
    public string VendorAddUnitTwo { get; set; }

    [Display(Name = "Full Address")]
    public string FullAddress
    {
        get
        {
            return $"{VendorAddNum} {TLRoadDirectionID} {VendorAddName} {TLRoadTypeID} {VendorAddUnitTypeOneID} {TLUnitTypeOne.UnitType} {VendorAddUnitOne} {VendorAddUnitTypeTwoID} {VendorAddUnitTwo}";
        }
    }

    [Display(Name = "City")]
    [StringLength(128)]
    public string VendorAddCity { get; set; }

    [Display(Name = "State")]
    public int? TLStateAcronymnID { get; set; }

    [Display(Name = "Zip Code")]
    [StringLength(20)]
    public string VendorZipCode { get; set; }

    [Display(Name = "Country")]
    public int? TLCountryID { get; set; }

    [Display(Name = "Active")]
    public bool VendorActive { get; set; }

    public virtual TLRoadDirection TLRoadDirection { get; set; }
    public virtual TLRoadType TLRoadType { get; set; }
    public virtual TLUnitType TLUnitTypeOne { get; set; }
    public virtual TLUnitType TLUnitTypeTwo { get; set; }
    public virtual TLStateAcronymn TLStateAcronymn { get; set; }
    public virtual TLCountry TLCountry { get; set; }
}

}


问:
How to make exact CSS3 Linear gradient like it's in the image?
For example this is gradient which I want to make in CSS3

I can use 1 px image cut and repeat in x axis as we were doing before. But now if I want to make same exact gradient using CSS3.

I know many free CSS3 Gradient Generator , Maker available on Internet. My questions is Which Generator is most preferred and how to make needed gradients with that.

Edit after Alex's answer : more Gradients

答:
Ultimate CSS Gradient Generator is my preferred choice.

```<code>background: #CCCCCC;
background: -moz-linear-gradient(top, #CCCCCC 0%, #343434 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#CCCCCC), color-stop(100%,#343434));
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#CCCCCC', endColorstr='#343434',GradientType=0 );
</code>```

It is rather easy to make the above gradient with. You can see it has one colour at top and steps to the next at bottom. Simply copy the top and bottom colour and place them into the relevant inputs on the generator.

ContentLicense: CC BY-SA 3.0

Body:
Ultimate CSS Gradient Generator is my preferred choice.

background: #CCCCCC;
background: -moz-linear-gradient(top, #CCCCCC 0%, #343434 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#CCCCCC), color-stop(100%,#343434));
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#CCCCCC', endColorstr='#343434',GradientType=0 );

It is rather easy to make the above gradient with. You can see it has one colour at top and steps to the next at bottom. Simply copy the top and bottom colour and place them into the relevant inputs on the generator.


问:
Removing an array of elements causes NullPointerException
I'm a beginner in Java. I have to make shopping cart. But I'm stuck on remove method.

Please help me!

I tried to make remove method. But it doesn't work well..

Customer.java

```<code>public class Customer {
    private String id;
    private String firstName;
    private String lastName;
    private ShoppingCart shopCart;

    public Customer(String id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        shopCart = new ShoppingCart();
    }

    public ShoppingCart getShopCart() {
        return this.shopCart;
    }
    @Override
    public String toString() {
        return ("Customer ID is: " + this.id + "\n" + "Customer's name is: "
                 + this.firstName + " " + this.lastName + "\n\n"
                + "Shopping list: \n\n" + shopCart.toString());

    }

</code>```

ShoppingCart.java

```<code>public class ShoppingCart {
    private int itemCount;
    private double totalPrice;
    private static int capacity;
    private Item[] cart;
    public ShoppingCart()
    {
        capacity = 5;
        itemCount = 0;
        totalPrice = 0.0;
        cart = new Item[capacity];
    }

    public void addBooktoCart(String title, String description, double price, int pageCount) {
        if (itemCount < 5) {

        Item item = new Book(title, description, price, pageCount);
        totalPrice += price;
        cart[itemCount] = item;
        itemCount += 1;
        }
        else {
            System.out.println("The maximum number that you can input is 5." +
                     "You cannot add item anymore");
        }

    }
//This remove method may be a problem.
    public void removeItemFromCart(String title) {
        boolean found = false;
        for (int i = 0; i < cart.length; i++) {
            if (cart[i] != null)
               {
                   if (cart[i].getTitle().equals(title)) {
                   totalPrice -= cart[i].getPrice();
                   cart[i] = null;
                   itemCount -= 1;
                   found = true;
                   }
               }
        }
if(found != true) {

            System.out.println("Item is not found in cart. Nothing removed");

        }
    }
    @Override
    public String toString() {
        String contents = "";

        for (int i = 0; i < itemCount; i++)
        {
            contents += cart[i].toString() + "\n\n";
        }

        String strDouble = String.format("%.2f", totalPrice);

        contents += "The total price is: $" + strDouble + "\n\n"
                + "How many items do you have in your shopping cart? \n"
                + "There are " + this.itemCount + " items in my shopping cart. "+ "\n";
        return contents;

    }
}

</code>```

Test.java

```<code>public class Test {
        public static void main(String[] args) {

        Customer c1 = new Customer("12345", "Hyun Min", "Lim");
        c1.getShopCart().addBooktoCart("Harry Potter", "Fantasy Genre", 10.99, 309);
        c1.getShopCart().addCDtoCart("Abbey Road", "Pop Genre", 6.50, 18);
        c1.getShopCart().addMovietoCart("Forrest Gump", "Drama Genre", 13.23, 141);
        c1.getShopCart().removeItemFromCart("Harry Potter");
        System.out.println(c1.toString());
    }
</code>```

I want to remove array of elements. But output is

Exception in thread "main" java.lang.NullPointerException.

What do I do if the compiler works well. I think that the remove method is a problem.

答:
The NullPointerException is probably caused by this line if (cart[i].getTitle().equals(title)) and not by accessing the cart object itself but invoking the equals method on a return value from getTitle. Check to make sure that the return value is not null before you compare it:

```<code>if (cart[i] != null)
{
    /* Assign the value to a local variable and then
     * check if it's null before accessing it.
     */
    String value = cart[i].getTitle();
    if (value != null && value.equals(title)) {
        totalPrice -= cart[i].getPrice();
        cart[i] = null;
        itemCount -= 1;
        found = true;
    }
}
</code>```

In any case you should be employing some kind of debugging technique to determine what exactly is causing your errors. Find out more about debugging in Java for Eclipse here and for IntelliJ here.

Body:
I'm a beginner in Java. I have to make shopping cart. But I'm stuck on remove method.

Please help me!

I tried to make remove method. But it doesn't work well..

Customer.java

public class Customer {
    private String id;
    private String firstName;
    private String lastName;
    private ShoppingCart shopCart;

    public Customer(String id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        shopCart = new ShoppingCart();
    }

    public ShoppingCart getShopCart() {
        return this.shopCart;
    }
    @Override
    public String toString() {
        return ("Customer ID is: " + this.id + "\n" + "Customer's name is: "
                 + this.firstName + " " + this.lastName + "\n\n"
                + "Shopping list: \n\n" + shopCart.toString());

    }

ShoppingCart.java

public class ShoppingCart {
    private int itemCount;
    private double totalPrice;
    private static int capacity;
    private Item[] cart;
    public ShoppingCart()
    {
        capacity = 5;
        itemCount = 0;
        totalPrice = 0.0;
        cart = new Item[capacity];
    }

    public void addBooktoCart(String title, String description, double price, int pageCount) {
        if (itemCount < 5) {

        Item item = new Book(title, description, price, pageCount);
        totalPrice += price;
        cart[itemCount] = item;
        itemCount += 1;
        }
        else {
            System.out.println("The maximum number that you can input is 5." +
                     "You cannot add item anymore");
        }

    }
//This remove method may be a problem.
    public void removeItemFromCart(String title) {
        boolean found = false;
        for (int i = 0; i < cart.length; i++) {
            if (cart[i] != null)
               {
                   if (cart[i].getTitle().equals(title)) {
                   totalPrice -= cart[i].getPrice();
                   cart[i] = null;
                   itemCount -= 1;
                   found = true;
                   }
               }
        }
if(found != true) {

            System.out.println("Item is not found in cart. Nothing removed");

        }
    }
    @Override
    public String toString() {
        String contents = "";

        for (int i = 0; i < itemCount; i++)
        {
            contents += cart[i].toString() + "\n\n";
        }

        String strDouble = String.format("%.2f", totalPrice);

        contents += "The total price is: $" + strDouble + "\n\n"
                + "How many items do you have in your shopping cart? \n"
                + "There are " + this.itemCount + " items in my shopping cart. "+ "\n";
        return contents;

    }
}

Test.java

public class Test {
        public static void main(String[] args) {

        Customer c1 = new Customer("12345", "Hyun Min", "Lim");
        c1.getShopCart().addBooktoCart("Harry Potter", "Fantasy Genre", 10.99, 309);
        c1.getShopCart().addCDtoCart("Abbey Road", "Pop Genre", 6.50, 18);
        c1.getShopCart().addMovietoCart("Forrest Gump", "Drama Genre", 13.23, 141);
        c1.getShopCart().removeItemFromCart("Harry Potter");
        System.out.println(c1.toString());
    }

I want to remove array of elements. But output is

Exception in thread "main" java.lang.NullPointerException.

What do I do if the compiler works well. I think that the remove method is a problem.

Body:
The NullPointerException is probably caused by this line if (cart[i].getTitle().equals(title)) and not by accessing the cart object itself but invoking the equals method on a return value from getTitle. Check to make sure that the return value is not null before you compare it:

if (cart[i] != null)
{
    /* Assign the value to a local variable and then
     * check if it's null before accessing it.
     */
    String value = cart[i].getTitle();
    if (value != null && value.equals(title)) {
        totalPrice -= cart[i].getPrice();
        cart[i] = null;
        itemCount -= 1;
        found = true;
    }
}

In any case you should be employing some kind of debugging technique to determine what exactly is causing your errors. Find out more about debugging in Java for Eclipse here and for IntelliJ here.


问:
What's the difference between casting a long to int versus using a bitwise AND in order to get the 4 least significant bytes?
I know that in order to get the 4 least significant bytes of a number of type long I can cast it to int/unsigned int or use a bitwise AND (& 0xFFFFFFFF).

This code produces the following output:

#include <stdio.h>

int main()
{
  long n = 0x8899AABBCCDDEEFF;
  printf("0x%016lX\n", n);
  printf("0x%016X\n", (int)n);
  printf("0x%016X\n", (unsigned int)n);
  printf("0x%016lX\n", n & 0xFFFFFFFF);
}
```

<p>Output:</p>

```0x8899AABBCCDDEEFF
0x00000000CCDDEEFF
0x00000000CCDDEEFF
0x00000000CCDDEEFF
```

Does that mean that the two methods used are equivalent? If so, do they always produce the same output regardless of the platform/compiler?

Also, is there any catch or pitfall while casting to unsigned int rather than int for the purpose of this question?

Finally, why is the output the same if you change the number n to be an unsigned long instead?

答:
What's the difference between casting a long to int versus using a bitwise AND in order to get the 4 least significant bytes?

Type.  Casting makes the value an int.  And'ing does not change the type.

Range.  Depending on int,long range, a cast may not change the value at all.

IDB and UB. implementation defined behavior and undefined behavior are present with mixing signed-ness.

To "get" the 4 LSBytes, use & 0xFFFFFFFFu or cast to uint32_t.

OP's question is unnecessarily convoluted.

long n = 0x8899AABBCCDDEEFF; --> Converting a value outside the range of a signed integer type is implementation-defined.

Otherwise, the new type is signed and the value cannot be represented in it; either the
  result is implementation-defined or an implementation-defined signal is raised.

  C11 §6.3.1.3 3

printf("0x%016lX\n", n); --> Printing a long with a "%lX" outside the the common range  of long/unsigned long is undefined behavior.

Let's go forward with unsigned long:

```<code>  unsigned long n = 0x8899AABBCCDDEEFF;  // no problem,
  printf("0x%016lX\n", n);               // no problem,
  printf("0x%016X\n", (int)n);           // problem, C11 6.3.1.3 3
  printf("0x%016X\n", (unsigned int)n);  // no problem,
  printf("0x%016lX\n", n & 0xFFFFFFFF);  // no problem,
</code>```

The "no problem" are OK even is unsigned long is 32-bit or 64-bit.  The output will differ, yet is OK.

Recall that int,long are not always 32,64 bit.  (16,32), (32,32), (32,64) are common.

int is at least 16 bit.

long is at least that of int and at least 32 bit.

Body:
I know that in order to get the 4 least significant bytes of a number of type long I can cast it to int/unsigned int or use a bitwise AND (& 0xFFFFFFFF).

This code produces the following output:

#include <stdio.h>

int main()
{
  long n = 0x8899AABBCCDDEEFF;
  printf("0x%016lX\n", n);
  printf("0x%016X\n", (int)n);
  printf("0x%016X\n", (unsigned int)n);
  printf("0x%016lX\n", n & 0xFFFFFFFF);
}

Output:

0x8899AABBCCDDEEFF
0x00000000CCDDEEFF
0x00000000CCDDEEFF
0x00000000CCDDEEFF

Does that mean that the two methods used are equivalent? If so, do they always produce the same output regardless of the platform/compiler?

Also, is there any catch or pitfall while casting to unsigned int rather than int for the purpose of this question?

Finally, why is the output the same if you change the number n to be an unsigned long instead?

Body:
What's the difference between casting a long to int versus using a bitwise AND in order to get the 4 least significant bytes?

Type.  Casting makes the value an int.  And'ing does not change the type.

Range.  Depending on int,long range, a cast may not change the value at all.

IDB and UB. implementation defined behavior and undefined behavior are present with mixing signed-ness.

To "get" the 4 LSBytes, use & 0xFFFFFFFFu or cast to uint32_t.

OP's question is unnecessarily convoluted.

long n = 0x8899AABBCCDDEEFF; --> Converting a value outside the range of a signed integer type is implementation-defined.

Otherwise, the new type is signed and the value cannot be represented in it; either the
  result is implementation-defined or an implementation-defined signal is raised.

  C11 §6.3.1.3 3

printf("0x%016lX\n", n); --> Printing a long with a "%lX" outside the the common range  of long/unsigned long is undefined behavior.

Let's go forward with unsigned long:

  unsigned long n = 0x8899AABBCCDDEEFF;  // no problem,
  printf("0x%016lX\n", n);               // no problem,
  printf("0x%016X\n", (int)n);           // problem, C11 6.3.1.3 3
  printf("0x%016X\n", (unsigned int)n);  // no problem,
  printf("0x%016lX\n", n & 0xFFFFFFFF);  // no problem,

The "no problem" are OK even is unsigned long is 32-bit or 64-bit.  The output will differ, yet is OK.

Recall that int,long are not always 32,64 bit.  (16,32), (32,32), (32,64) are common.

int is at least 16 bit.

long is at least that of int and at least 32 bit.


问:
Java create tar archive with entries of unknown size
I have a web app where I need to be able to serve the user an archive of multiple files. I've set up a generic ArchiveExporter, and made a ZipArchiveExporter. Works beautifully! I can stream my data to my server, and archive the data and stream it to the user all without using much memory, and without needing a filesystem (I'm on Google App Engine).

Then I remembered about the whole zip64 thing with 4gb zip files. My archives can get potentially very large (high res images), so I'd like to have an option to avoid zip files for my larger input.

I checked out org.apache.commons.compress.archivers.tar.TarArchiveOutputStream and thought I had found what I needed! Sadly when I checked the docs, and ran into some errors; I quickly found out you MUST pass the size of each entry as you stream. This is a problem because the data is being streamed to me with no way of knowing the size beforehand.

I tried counting and returning the written bytes from export(), but TarArchiveOutputStream expects a size in TarArchiveEntry before writing to it, so that obviously doesn't work.

I can use a ByteArrayOutputStream and read each entry entirely before writing its content so I know its size, but my entries can pontentially get very large; and this is not very polite to the other processes running on the instance.

I could use some form of persistence, upload the entry, and query the data size. However, that would be a waste of my google storage api calls, bandwidth, storage, and runtime.

I am aware of this SO question asking almost the same thing, but he settled for using zip files and there is no more relevant information.

What is the ideal solution to creating a tar archive with entries of unknown size?

```<code>public abstract class ArchiveExporter<T extends OutputStream> extends Exporter { //base class
    public abstract void export(OutputStream out); //from Exporter interface
    public abstract void archiveItems(T t) throws IOException;
}

public class ZipArchiveExporter extends ArchiveExporter<ZipOutputStream> { //zip class, works as intended
    @Override
    public void export(OutputStream out) throws IOException {
        try(ZipOutputStream zos = new ZipOutputStream(out, Charsets.UTF_8)) {
            zos.setLevel(0);
            archiveItems(zos);
        }
    }
    @Override
    protected void archiveItems(ZipOutputStream zos) throws IOException {
        zos.putNextEntry(new ZipEntry(exporter.getFileName()));
        exporter.export(zos);
        //chained call to export from other exporter like json exporter for instance
        zos.closeEntry();
    }
}

public class TarArchiveExporter extends ArchiveExporter<TarArchiveOutputStream> {
    @Override
    public void export(OutputStream out) throws IOException {
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(out, "UTF-8")) {
            archiveItems(taos);
        }
    }
    @Override
    protected void archiveItems(TarArchiveOutputStream taos) throws IOException {
        TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
        //entry.setSize(?);
        taos.putArchiveEntry(entry);
        exporter.export(taos);
        taos.closeArchiveEntry();
    }
}
</code>```

EDIT this is what I was thinking with the ByteArrayOutputStream. It works, but I cannot guarantee I will always have enough memory to store the whole entry at once, hence my streaming efforts. There has to be a more elegant way of streaming a tarball! Maybe this is a question more suited for Code Review?

```<code>protected void byteArrayOutputStreamApproach(TarArchiveOutputStream taos) throws IOException {
    TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
    try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        exporter.export(baos);
        byte[] data = baos.toByteArray();
        //holding ENTIRE entry in memory. What if it's huge? What if it has more than Integer.MAX_VALUE bytes? :[
        int len = data.length;
        entry.setSize(len);
        taos.putArchiveEntry(entry);
        taos.write(data);
        taos.closeArchiveEntry();
    }
}
</code>```

EDIT This is what I meant by uploading the entry to a medium (Google Cloud Storage in this case) to accurately query the whole size. Seems like major overkill for what seems like a simple problem, but this doesn't suffer from the same ram problems as the solution above. Just at the cost of bandwidth and time. I hope someone smarter than me comes by and makes me feel stupid soon :D

```<code>protected void googleCloudStorageTempFileApproach(TarArchiveOutputStream taos) throws IOException {
    TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
    String name = NameHelper.getRandomName(); //get random name for temp storage
    BlobInfo blobInfo = BlobInfo.newBuilder(StorageHelper.OUTPUT_BUCKET, name).build(); //prepare upload of temp file
    WritableByteChannel wbc = ApiContainer.storage.writer(blobInfo); //get WriteChannel for temp file
    try(OutputStream out = Channels.newOutputStream(wbc)) {
        exporter.export(out); //stream items to remote temp file
    } finally {
        wbc.close();
    }

    Blob blob = ApiContainer.storage.get(blobInfo.getBlobId());
    long size = blob.getSize(); //accurately query the size after upload
    entry.setSize(size);
    taos.putArchiveEntry(entry);

    ReadableByteChannel rbc = blob.reader(); //get ReadChannel for temp file
    try(InputStream in = Channels.newInputStream(rbc)) {
        IOUtils.copy(in, taos); //stream back to local tar stream from remote temp file
    } finally {
        rbc.close();
    }
    blob.delete(); //delete remote temp file

    taos.closeArchiveEntry();
}
</code>```

答:
I've been looking at a similar issue, and this is a constraint of tar file format, as far as I can tell.

Tar files are written as a stream, and metadata (filenames, permissions etc) are written between the file data (i.e. metadata 1, filedata 1, metadata 2, filedata 2 etc). The program that extracts the data, it reads metadata 1, then starts extracting filedata 1, but it has to have a way of knowing when it's done. This could be done a number of ways; tar does this by having the length in the metadata.

Depending on your needs, and what the recipient expects out, there are a few options that I can see (not all apply to your situation):

As you mentioned, load an entire file, work out the length, then send it.

Divide the file into blocks, of predefined length (which fits into memory), then tar them up as file1-part1, file1-part2 etc.; the last block would be short.

Divide the file into blocks of a predefined length (which don't need to fit into memory), then pad the last block to that size with something appropriate.

Work out the maximum possible size of the file, and pad to that size.

Use a different archive format.

Make your own archive format, which does not have this limitation.

Interestingly, gzip does not have predefined limits, and multiple gzips can be concatenated together, each with it's own "original filename". Unfortunately, standard gunzip extracts all the resulting data into one file, using the (?) first filename.

Body:
I have a web app where I need to be able to serve the user an archive of multiple files. I've set up a generic ArchiveExporter, and made a ZipArchiveExporter. Works beautifully! I can stream my data to my server, and archive the data and stream it to the user all without using much memory, and without needing a filesystem (I'm on Google App Engine).

Then I remembered about the whole zip64 thing with 4gb zip files. My archives can get potentially very large (high res images), so I'd like to have an option to avoid zip files for my larger input.

I checked out org.apache.commons.compress.archivers.tar.TarArchiveOutputStream and thought I had found what I needed! Sadly when I checked the docs, and ran into some errors; I quickly found out you MUST pass the size of each entry as you stream. This is a problem because the data is being streamed to me with no way of knowing the size beforehand.

I tried counting and returning the written bytes from export(), but TarArchiveOutputStream expects a size in TarArchiveEntry before writing to it, so that obviously doesn't work.

I can use a ByteArrayOutputStream and read each entry entirely before writing its content so I know its size, but my entries can pontentially get very large; and this is not very polite to the other processes running on the instance.

I could use some form of persistence, upload the entry, and query the data size. However, that would be a waste of my google storage api calls, bandwidth, storage, and runtime.

I am aware of this SO question asking almost the same thing, but he settled for using zip files and there is no more relevant information.

What is the ideal solution to creating a tar archive with entries of unknown size?

public abstract class ArchiveExporter<T extends OutputStream> extends Exporter { //base class
    public abstract void export(OutputStream out); //from Exporter interface
    public abstract void archiveItems(T t) throws IOException;
}

public class ZipArchiveExporter extends ArchiveExporter<ZipOutputStream> { //zip class, works as intended
    @Override
    public void export(OutputStream out) throws IOException {
        try(ZipOutputStream zos = new ZipOutputStream(out, Charsets.UTF_8)) {
            zos.setLevel(0);
            archiveItems(zos);
        }
    }
    @Override
    protected void archiveItems(ZipOutputStream zos) throws IOException {
        zos.putNextEntry(new ZipEntry(exporter.getFileName()));
        exporter.export(zos);
        //chained call to export from other exporter like json exporter for instance
        zos.closeEntry();
    }
}

public class TarArchiveExporter extends ArchiveExporter<TarArchiveOutputStream> {
    @Override
    public void export(OutputStream out) throws IOException {
        try(TarArchiveOutputStream taos = new TarArchiveOutputStream(out, "UTF-8")) {
            archiveItems(taos);
        }
    }
    @Override
    protected void archiveItems(TarArchiveOutputStream taos) throws IOException {
        TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
        //entry.setSize(?);
        taos.putArchiveEntry(entry);
        exporter.export(taos);
        taos.closeArchiveEntry();
    }
}

EDIT this is what I was thinking with the ByteArrayOutputStream. It works, but I cannot guarantee I will always have enough memory to store the whole entry at once, hence my streaming efforts. There has to be a more elegant way of streaming a tarball! Maybe this is a question more suited for Code Review?

protected void byteArrayOutputStreamApproach(TarArchiveOutputStream taos) throws IOException {
    TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
    try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        exporter.export(baos);
        byte[] data = baos.toByteArray();
        //holding ENTIRE entry in memory. What if it's huge? What if it has more than Integer.MAX_VALUE bytes? :[
        int len = data.length;
        entry.setSize(len);
        taos.putArchiveEntry(entry);
        taos.write(data);
        taos.closeArchiveEntry();
    }
}

EDIT This is what I meant by uploading the entry to a medium (Google Cloud Storage in this case) to accurately query the whole size. Seems like major overkill for what seems like a simple problem, but this doesn't suffer from the same ram problems as the solution above. Just at the cost of bandwidth and time. I hope someone smarter than me comes by and makes me feel stupid soon :D

protected void googleCloudStorageTempFileApproach(TarArchiveOutputStream taos) throws IOException {
    TarArchiveEntry entry = new TarArchiveEntry(exporter.getFileName());
    String name = NameHelper.getRandomName(); //get random name for temp storage
    BlobInfo blobInfo = BlobInfo.newBuilder(StorageHelper.OUTPUT_BUCKET, name).build(); //prepare upload of temp file
    WritableByteChannel wbc = ApiContainer.storage.writer(blobInfo); //get WriteChannel for temp file
    try(OutputStream out = Channels.newOutputStream(wbc)) {
        exporter.export(out); //stream items to remote temp file
    } finally {
        wbc.close();
    }

    Blob blob = ApiContainer.storage.get(blobInfo.getBlobId());
    long size = blob.getSize(); //accurately query the size after upload
    entry.setSize(size);
    taos.putArchiveEntry(entry);

    ReadableByteChannel rbc = blob.reader(); //get ReadChannel for temp file
    try(InputStream in = Channels.newInputStream(rbc)) {
        IOUtils.copy(in, taos); //stream back to local tar stream from remote temp file
    } finally {
        rbc.close();
    }
    blob.delete(); //delete remote temp file

    taos.closeArchiveEntry();
}


问:
mongoose push to array
I need to push an object into an array into the database, but I get an error. What am I doing wrong ? I found such a topic Push items into mongo array via mongoose but can not properly apply
..................................................................................................................................

```<code>habalka.files.push({_id: "tata", destination: "Harry"});
    habalka.save((res) => {
      console.log(res)
    });
</code>```

when I try this code i get this answer

MongooseError: document must have an _id before saving

database

```<code>{
 "_id": "78935e75-86c0-47c2-a76d-2b52a0a71e7c",
    "files": [
        {
            "_id": "67e848e5-7fb0-480b-a1d3-f3a09d1b57f7",
            "destination": "./uploads/78935e75-86c0-47c2-a76d-2b52a0a71e7c",
            "filename": "67e848e5-7fb0-480b-a1d3-f3a09d1b57f7.mp3",
            "path": "uploads\\78935e75-86c0-47c2-a76d-2b52a0a71e7c\\67e848e5-7fb0-480b-a1d3-f3a09d1b57f7.mp3",
            "folder": "78935e75-86c0-47c2-a76d-2b52a0a71e7c",
            "info": {
                "size": 12120000,
                "mimetype": "audio/mp3",
                "encoding": "7bit",
                "originalname": "Попрошайка Евгений Владимирович из ФАСП. Выпуск 10.mp3",
                "fieldname": "selectedFile"
            },
            "date": {
                "$date": "2019-05-23T01:07:27.122Z"
            }
        }
    ],
    "date": {
        "$date": "2019-05-23T01:07:27.123Z"
    },
    "__v": 0
}
</code>```

Schema

```<code>const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const HabalkaSchema = new Schema({
  _id: {
    type: String
  },
  files: [
    {
      _id: {
        type: String
      },
      destination: {
        type: String
      },
      filename: {
        type: String
      },
      path: {
        type: String
      },
      folder: {
        type: String
      },
      info: {
        size: {
          type: Number
        },
        mimetype: {
          type: String
        },
        encoding: {
          type: String
        },
        originalname: {
          type: String
        },
        fieldname: {
          type: String
        },
      },
      date: {
        type: Date,
        default: Date.now
      }
    }
  ],
  date: {
    type: Date,
    default: Date.now
  }
});
module.exports = Habalka = mongoose.model('habalka', HabalkaSchema);
</code>```

request

```<code>let Habalka = habalka.findOne({_id: "78935e75-86c0-47c2-a76d-2b52a0a71e7c"});
Habalka.files.push({destination: "Harry"});
Habalka.save();
</code>```

答:
I found this solution and this work.</p>

```<code>Habalka.findById("78935e75-86c0-47c2-a76d-2b52a0a71e7c")
  .then(post => {
    console.log(post)
    post.files.unshift({_id: "tata", destination: "vava"});
    post.save().then(res => console.log(res));
  })
  .catch(err => res.status(404).json({ postnotfound: 'No post found' }));
</code>```

Title: mongoose push to array

Body:
I need to push an object into an array into the database, but I get an error. What am I doing wrong ? I found such a topic Push items into mongo array via mongoose but can not properly apply
..................................................................................................................................

habalka.files.push({_id: "tata", destination: "Harry"});
    habalka.save((res) => {
      console.log(res)
    });

when I try this code i get this answer

MongooseError: document must have an _id before saving

database

{
 "_id": "78935e75-86c0-47c2-a76d-2b52a0a71e7c",
    "files": [
        {
            "_id": "67e848e5-7fb0-480b-a1d3-f3a09d1b57f7",
            "destination": "./uploads/78935e75-86c0-47c2-a76d-2b52a0a71e7c",
            "filename": "67e848e5-7fb0-480b-a1d3-f3a09d1b57f7.mp3",
            "path": "uploads\\78935e75-86c0-47c2-a76d-2b52a0a71e7c\\67e848e5-7fb0-480b-a1d3-f3a09d1b57f7.mp3",
            "folder": "78935e75-86c0-47c2-a76d-2b52a0a71e7c",
            "info": {
                "size": 12120000,
                "mimetype": "audio/mp3",
                "encoding": "7bit",
                "originalname": "Попрошайка Евгений Владимирович из ФАСП. Выпуск 10.mp3",
                "fieldname": "selectedFile"
            },
            "date": {
                "$date": "2019-05-23T01:07:27.122Z"
            }
        }
    ],
    "date": {
        "$date": "2019-05-23T01:07:27.123Z"
    },
    "__v": 0
}

Schema

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const HabalkaSchema = new Schema({
  _id: {
    type: String
  },
  files: [
    {
      _id: {
        type: String
      },
      destination: {
        type: String
      },
      filename: {
        type: String
      },
      path: {
        type: String
      },
      folder: {
        type: String
      },
      info: {
        size: {
          type: Number
        },
        mimetype: {
          type: String
        },
        encoding: {
          type: String
        },
        originalname: {
          type: String
        },
        fieldname: {
          type: String
        },
      },
      date: {
        type: Date,
        default: Date.now
      }
    }
  ],
  date: {
    type: Date,
    default: Date.now
  }
});
module.exports = Habalka = mongoose.model('habalka', HabalkaSchema);

request

let Habalka = habalka.findOne({_id: "78935e75-86c0-47c2-a76d-2b52a0a71e7c"});
Habalka.files.push({destination: "Harry"});
Habalka.save();

Body:
I found this solution and this work.

Habalka.findById("78935e75-86c0-47c2-a76d-2b52a0a71e7c")
  .then(post => {
    console.log(post)
    post.files.unshift({_id: "tata", destination: "vava"});
    post.save().then(res => console.log(res));
  })
  .catch(err => res.status(404).json({ postnotfound: 'No post found' }));


问:
How write to csv file this data correctly = "type" not """ type """
I've tried to write this data to csv file but it writes not correctly. I want receive "type" not """type"""

python3, pycharm

```<code> import pandas as pd
 import csv

 def main():
    req = pd.read_csv('/Users/user/web/python/Book copy.csv')
    account = 'test'
    transferType = 'Loan'
    comment = "test"

    with open('data2.csv', 'w') as csv_file:
        writer = csv.writer(csv_file, delimiter=';')
        writer.writerow(['"type"','"currency"','"amount"','"comment"'])

        for i in range(len(req)):
            currency = req['Currency'].values[i]
            reqAmount = req['Request'].values[i]
            r = round(reqAmount, -1)
            data = [transferType, account, currency, r, comment]
            writer.writerow(data)

    print("DONE")

if __name__ == '__main__':
    main()
</code>```

"""type"""

答:
On top of what the @Iain Shelvington mentioned about escaping the ", I would also change your delimeter to ',' if you want a spreadsheet program like Excel to read the output on opening as a formatted spreadsheet. Moreover, if you don't want the " characters to appear in the column headers of the csv then remove them from the list as such:

```<code>writer.writerow(['type','currency','amount','comment'])
</code>```

Body:
I've tried to write this data to csv file but it writes not correctly. I want receive "type" not """type"""

python3, pycharm

 import pandas as pd
 import csv

 def main():
    req = pd.read_csv('/Users/user/web/python/Book copy.csv')
    account = 'test'
    transferType = 'Loan'
    comment = "test"

    with open('data2.csv', 'w') as csv_file:
        writer = csv.writer(csv_file, delimiter=';')
        writer.writerow(['"type"','"currency"','"amount"','"comment"'])

        for i in range(len(req)):
            currency = req['Currency'].values[i]
            reqAmount = req['Request'].values[i]
            r = round(reqAmount, -1)
            data = [transferType, account, currency, r, comment]
            writer.writerow(data)

    print("DONE")

if __name__ == '__main__':
    main()

"""type"""

Body:
On top of what the @Iain Shelvington mentioned about escaping the ", I would also change your delimeter to ',' if you want a spreadsheet program like Excel to read the output on opening as a formatted spreadsheet. Moreover, if you don't want the " characters to appear in the column headers of the csv then remove them from the list as such:

writer.writerow(['type','currency','amount','comment'])


问:
How to get MST properties from vbscript
So, I am creating a vbscript that will read an MSI and MST file. The idea is that if the user that will run the script is testing an MSI with an MST file involved, the script should create a "report" of the new properties that this MST have.

I am able to get the properties from a regular MSI, the problem is when I am trying to get into the MST section. While doing research I found out about the _TransformView Table and this should help me to obtain this information but I think I am not sure I know how to handle that table.

```<code>Const msiTransformErrorViewTransform = 256

Const msiOpenDB = 2

Dim FS, TS, WI, DB, View, Rec

Set WI = CreateObject("WindowsInstaller.Installer")

Set DB = WI.OpenDatabase(msiPath,msiOpenDB)

DB.ApplyTransform mstPath, msiTransformErrorViewTransform

If Err.number Then
    Exit Function
End If

For i = 0 To 24 'Number of properties on the arrPropertyList

    Set View = DB.OpenView("Select `Value` From Property WHERE `Property` = " & "'" & arrPropertyList(i) & "'")
    View.Execute
    Set Rec = View.Fetch
    If Not Rec Is Nothing Then
        objLog.WriteLine arrPropertyList(i) & " = " & Rec.StringData(1)
    End If

Next
</code>```

That code will display the msi properties that I have added on the arrPropertyList. The thing is that I am looking for the MST properties and I am only getting the MSI ones. I know that I should change the Query to access the _TransformView Table when calling the DB.OpenView but not sure how can I get to this information! Any knowledge you can share would be welcome.

答:
WiLstXfm.vbs: Are you familiar with the MSI SDK sample: wilstxfm.vbs (View a Transform)? It can be used to view transform files. Usage is as follows:

```<code>cscript.exe WiLstXfm.vbs MySetup.msi MySetup.mst
</code>```

Mock-up output:

```<code>Property Value           [INSTALLLEVEL] {100}->{102}
File DELETE              [Help.chm]
</code>```

I think all you need is in there? Maybe give it a quick look. There is a whole bunch of such MSI API Samples - for all kinds of MSI purposes.

Github.com / Windows SDK: These VBScripts are installed with the Windows SDK, so you can find them on your local disk if you have Visual Studio installed, but you can also find them on Github.com:

Github: WiLstXfm.vbs - Microsoft repository on github.com.

Disk: On your local disk, search under Program Files (x86) if you have Visual Studio installed. Current Example: %ProgramFiles(x86)%\Windows Kits\10\bin\10.0.17763.0\x86.

Title: How to get MST properties from vbscript

Body:
So, I am creating a vbscript that will read an MSI and MST file. The idea is that if the user that will run the script is testing an MSI with an MST file involved, the script should create a "report" of the new properties that this MST have.

I am able to get the properties from a regular MSI, the problem is when I am trying to get into the MST section. While doing research I found out about the _TransformView Table and this should help me to obtain this information but I think I am not sure I know how to handle that table.

Const msiTransformErrorViewTransform = 256

Const msiOpenDB = 2

Dim FS, TS, WI, DB, View, Rec

Set WI = CreateObject("WindowsInstaller.Installer")

Set DB = WI.OpenDatabase(msiPath,msiOpenDB)

DB.ApplyTransform mstPath, msiTransformErrorViewTransform

If Err.number Then
    Exit Function
End If

For i = 0 To 24 'Number of properties on the arrPropertyList

    Set View = DB.OpenView("Select `Value` From Property WHERE `Property` = " & "'" & arrPropertyList(i) & "'")
    View.Execute
    Set Rec = View.Fetch
    If Not Rec Is Nothing Then
        objLog.WriteLine arrPropertyList(i) & " = " & Rec.StringData(1)
    End If

Next

That code will display the msi properties that I have added on the arrPropertyList. The thing is that I am looking for the MST properties and I am only getting the MSI ones. I know that I should change the Query to access the _TransformView Table when calling the DB.OpenView but not sure how can I get to this information! Any knowledge you can share would be welcome.

Body:
WiLstXfm.vbs: Are you familiar with the MSI SDK sample: wilstxfm.vbs (View a Transform)? It can be used to view transform files. Usage is as follows:

cscript.exe WiLstXfm.vbs MySetup.msi MySetup.mst

Mock-up output:

Property Value           [INSTALLLEVEL] {100}->{102}
File DELETE              [Help.chm]

I think all you need is in there? Maybe give it a quick look. There is a whole bunch of such MSI API Samples - for all kinds of MSI purposes.

Github.com / Windows SDK: These VBScripts are installed with the Windows SDK, so you can find them on your local disk if you have Visual Studio installed, but you can also find them on Github.com:

Github: WiLstXfm.vbs - Microsoft repository on github.com.

Disk: On your local disk, search under Program Files (x86) if you have Visual Studio installed. Current Example: %ProgramFiles(x86)%\Windows Kits\10\bin\10.0.17763.0\x86.


问:
Map Object Array to a Bean
I am making an SQL query via JPA and getting a List of Object Arrays. I wish to map these Object Arrays into a bean.

For example, my query gives me the following output.

```<code>List<Object[]> list = // myWorkingJpaQuery;
// list is of length 2.
// Each Object array always holds a Long in index 0,
// a TimeStamp in index 1 and a String in index 2.
</code>```

Instead of reading these values and performing casting, I wish to map it to a class as follows:

```<code>class ExampleClass{
    //all these variables matches the aliases in myWorkingJpaQuery.
    Long id;
    TimeStamp ts;
    String name;
    // get set
}
</code>```

Tried to use the above class my changing the JPA methods return type and assigning it in the calling class as follows but it doesn't work.

```<code>List<ExampleClass> list = // myWorkingJpaQuery with List<ExampleClass> as return type;
</code>```

Is there a way to do this? It is currently working fine if I stick to Object Array but just trying not to use Objects and castings. For reference, I am using Spring.

答:
Does your ExampleClass have a constructor ?
if yes you should be able to do the following :

```<code>List<ExampleClass> myList = new ArrayList<ExampleClass>();
List<Object[]> list = // myWorkingJpaQuery;
for (int i = 0; i < list.size(); i++) {
        ExampleClass obj = new ExampleClass(list.get(i)[0],list.get(i)[1],list.get(i)[2]);
        myList.add(obj);
    }
</code>```

And your done

Title: Map Object Array to a Bean

Body:
I am making an SQL query via JPA and getting a List of Object Arrays. I wish to map these Object Arrays into a bean.

For example, my query gives me the following output.

List<Object[]> list = // myWorkingJpaQuery;
// list is of length 2.
// Each Object array always holds a Long in index 0,
// a TimeStamp in index 1 and a String in index 2.

Instead of reading these values and performing casting, I wish to map it to a class as follows:

class ExampleClass{
    //all these variables matches the aliases in myWorkingJpaQuery.
    Long id;
    TimeStamp ts;
    String name;
    // get set
}

Tried to use the above class my changing the JPA methods return type and assigning it in the calling class as follows but it doesn't work.

List<ExampleClass> list = // myWorkingJpaQuery with List<ExampleClass> as return type;

Is there a way to do this? It is currently working fine if I stick to Object Array but just trying not to use Objects and castings. For reference, I am using Spring.

Body:
Does your ExampleClass have a constructor ?
if yes you should be able to do the following :

List<ExampleClass> myList = new ArrayList<ExampleClass>();
List<Object[]> list = // myWorkingJpaQuery;
for (int i = 0; i < list.size(); i++) {
        ExampleClass obj = new ExampleClass(list.get(i)[0],list.get(i)[1],list.get(i)[2]);
        myList.add(obj);
    }

And your done


问:
Extracting the dimensions of a rectangle
So I'm working on an image processing project with sonar images. To be more specific, I am trying to extract the dimensions of an image of a pool taken by a sonar scanner. I was able to extract the rectangular region of the pool but I can not figure out how to get the dimensions of each edge in terms of pixels. I am working with OpenCV in Python.

I would appreciate if anyone would give me any suggestion on how this could be done.

I already tried finding the line intersections of hough lines but that did not have promising results.

Code so far.

import cv2
import numpy as np
from scipy import ndimage as ndi
from scipy.ndimage.measurements import label

def largest_component(indices):
    #this function takes a list of indices denoting
    #the white regions of the image and returns the largest
    #white object of connected indices

    return_arr = np.zeros((512,512), dtype=np.uint8)
    for index in indeces:
        return_arr[index[0]][index[1]] = 255

    return return_arr

image = cv2.imread('sonar_dataset/useful/sonarXY_5.bmp', 0)
image_gaussian = ndi.gaussian_filter(image, 4)
image_gaussian_inv = cv2.bitwise_not(image_gaussian)
kernel = np.ones((3,3),np.uint8)

# double thresholding extracting the sides of the rectangle
ret1, img1 = cv2.threshold(image_gaussian_inv, 120, 255, cv2.THRESH_BINARY)
ret2, img2 = cv2.threshold(image_gaussian_inv, 150, 255, cv2.THRESH_BINARY)

double_threshold = img1 - img2
closing = cv2.morphologyEx(double_threshold, cv2.MORPH_CLOSE, kernel1)

labeled, ncomponents = label(closing, kernel)
indices = np.indices(closing.shape).T[:,:,[1, 0]]
twos = indices[labeled == 2]
area =[np.sum(labeled==val) for val in range(ncomponents+1)]

rectangle = largest_component(twos)

cv2.imshow('rectangle', rectangle)
cv2.waitKey(0)
```

The original image and extracted object are below.

答:
So here's what I came up with - it's a bit labour intensive but it does get us to the right answer eventually.  I will be directly using the connected components output that you have shown with the last image.

Use morphological image skeletonization so that we get the skeleton of the blob.  This way, it'll give us the most minimal contour representation such that we get a one pixel wide boundary that goes through the middle of each thick edge.  You can achieve this through Scikit-image's skeletonize method.

Use the Hough Transform which is a line detection method on the skeletonized image.  In summary, it parameterizes lines in the polar domain and the output would be a set of rho and theta that tell us which lines are detected in the skeletonized image.  We can use OpenCV's cv2.HoughLines for that.  It is very important that you do this on the skeletonized image or we will have a lot of candidate lines parallel to where the true delineation of the bounding box is and you wouldn't be able to distinguish between them.

Take each pair of lines and find their point of intersection.  We'd expect that with all pairs of lines, there will be 4 predominant clusters of intersections that give us the corner of each rectangle.

Because of the noise in the contours, we may get more than four points of intersection.  We can use the convex hull to finally get 4 points of intersection for the rectangle.  In summary, the convex hull algorithm operates on a list of points where it defines a subset of points that can minimally encompass the list of points.  We can use cv2.convexHull.

Finally, due to the quantization of the Hough Transform, there may be multiple points that are within the vicinity of each corner.  Therefore, apply K-Means clustering to find 4 clusters of points and thus find their centroids.  We can use cv2.kmeans for that.

Once we find the centroids, we can simply iterate through each pair of points in a cyclical fashion to finally find the distances to each corner and thus find the distances you care about.

Let's step through each point one by one:

Step #1 - Morphological Image Skeletonization

Using Scikit-image's skeletonize, we can skeletonize the connected components image that you've shown above.  Take note that you need to convert the image to binary before you proceed. Once you call the method, we'll need to convert back to unsigned 8-bit integer after for the rest of the process. I've downloaded the image above and saved it locally.  We can run the skeletonize method after:

```<code>from skimage.morphology import skeletonize

im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)

# Convert to uint8
out = 255*(out.astype(np.uint8))
</code>```

We get this image:

Step #2 - Use the Hough Transform

Using the Hough Transform, we can detect the most prominent lines in this image:

```<code>lines = cv2.HoughLines(out,1,np.pi/180,60)
</code>```

Here we specify the search space so that we look for lines where the bin size has a length of 1 and the angles have a bin of 1 degree, or pi / 180 radians.  In summary, the Hough Transform looks at each edge point and iterates through a range of angles theta that are subtended from the origin to each edge point and calculate the corresponding value of rho respecting the bin size.  This pair gets logged into a 2D histogram and we register a vote.  We threshold this 2D histogram so that any bins beyond a certain value are line candidates.  In the above line of code, set the threshold for the bin counts to be 60.

This code is optional, but I wanted to show you what the visualized lines look like:

```<code>img_colour = np.dstack([im, im, im])
lines = cv2.HoughLines(edges,1,np.pi/180,60)
for rho,theta in lines[:,0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv2.line(img_colour,(x1,y1),(x2,y2),(0,0,255),2)
</code>```

This code I pulled from the following tutorial.  It draws the Hough Transform detected lines in the image as red.  I get the following image:

As we can see, there are four points of intersection in the image.  It's our job next to find these points of intersection.

Step #3 - Find points of intersection

In the Hough Transform, we can relate the length of the line from the origin to a point (x, y) in the image subtended at the angle theta by:

```<code>rho = x*cos(theta) + y*sin(theta)
</code>```

We can also form the equation of the line y = m*x + c in Cartesian form.  We can transform between the two by dividing both sides of the rho equation by sin(theta) then moving the relevant terms to each side:

Therefore, we should cycle through all unique pairs of lines and using the above equation, we can find their point of intersection by setting their Cartesian forms to be equal to each other.  This I won't derive for you for the interest of saving space, but simply set two lines in Cartesian form equal to each other and solve for the x coordinate of intersection.  Once that's done, substitute this point into any of the two lines to find the y coordinate.  We should obviously skip intersection points that go outside of the image in the case of two nearly parallel lines or if we choose two pairs of lines that go in the same direction and don't intersect.

```<code>pts = []
for i in range(lines.shape[0]):
    (rho1, theta1) = lines[i,0]
    m1 = -1/np.tan(theta1)
    c1 = rho1 / np.sin(theta1)
    for j in range(i+1,lines.shape[0]):
        (rho2, theta2) = lines[j,0]
        m2 = -1 / np.tan(theta2)
        c2 = rho2 / np.sin(theta2)
        if np.abs(m1 - m2) <= 1e-8:
            continue
        x = (c2 - c1) / (m1 - m2)
        y = m1*x + c1
        if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
            pts.append((int(x), int(y)))
</code>```

pts is a list of tuples such that we add all points of intersection that are within the image that are not out of bounds.

Step #4 - Use the Convex Hull

We can use this list of tuples and use the convex hull so that we find a list of points that define the outer perimeter of the rectangle. Take note that the order of points defining the rectangle are counter-clockwise. This doesn't matter for this step but it will matter later:

```<code>pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)
</code>```

hull contains a 3D NumPy array that is a subset of the original points of intersection that create the outer boundary of the image.  We can use these points to draw where these are located in the image for illustration

```<code>out2 = np.dstack([im, im, im])
for pt in hull[:,0]:
    cv2.circle(out2, tuple(pt), 2, (0, 255, 0), 2)
</code>```

I've taken the original image and drawn the corner points in green.  We get this image:

Step #5 - Apply K-Means clustering

As you can see in the image above, there are multiple points that map to each corner.  It would be good if we can consolidate the multiple points at each corner into a single point.  One way is to average all of the points in each corner and the easiest way to do that out of box is to use K-Means clustering.  We need the centroids to thus give us the final corner points of the rectangle.  We need to make sure we specify 4 clusters to find.

From the K-Means clustering tutorial from the OpenCV docs, we can use this code:

```<code># Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS

# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)
</code>```

The first parameter is the convex hull of points that need to be in float32 as required by the algorithm.  The second parameter specifies the number of clusters we want to search for, so 4 in our case.  The third parameter you can skip.
 It is a placeholder for the best cluster ID each point is assigned to but we don't need to use it.  criteria are the K-Means parameters used for the mechanics of the algorithm, and the fifth parameter tells us how many attempts we should run to find the best clusters.  We choose 10, meaning that we run K-Means 10 times and choose the clustering configuration that has the least amount of error.  The error is stored in the compactness variable that is output from the algorithm.  Finally, the last variable are optional flags and we set this so that the initial centroids of the algorithm are simply selected at random from the points.

labels provides which cluster ID is assigned to each point and centers is the key variable we need which thus returns:

```<code>array([[338.5   , 152.5   ],
       [302.6667, 368.6667],
       [139.    , 340.    ],
       [178.5   , 127.    ]], dtype=float32)
</code>```

These are the four corner points of the rectangle.  We can see where these line up by drawing them directly on the original image, and we also get this image:

```<code>out3 = np.dstack([im, im, im])
for pt in centers:
    cv2.circle(out3, tuple(pt), 2, (0, 255, 0), 2)
</code>```

Step #6 - Measure the lengths now

Finally, we can cycle through each pair of lines and find the corresponding dimensions.  Take note that because K-Means has the centroids in random order due to the random nature of the algorithm, we can run the convex hull on these centroids to ensure that the order is circular.

```<code>lines = cv2.HoughLines(out,1,np.pi/180,60)
</code>```0

We thus get:

```<code>lines = cv2.HoughLines(out,1,np.pi/180,60)
</code>```1

If you want perspective to see how the bounding box lines up, let's actually draw these lines on the image that are defined at these centres:

```<code>lines = cv2.HoughLines(out,1,np.pi/180,60)
</code>```2

We get:

To see where this lines up with the original image, let's just repeat the code above but drawing the lines on the original image.  I downloaded a copy of the original image to do so:

```<code>out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)
</code>```

For the sake of completeness, here's the entire code from start to finish without all of the debug outputs - we go from reading the image to drawing the lines in the original image with printing the lengths of each side in the detected rectangle.

```<code>from skimage.morphology import skeletonize
import cv2
import numpy as np

# Step #1 - Skeletonize
im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)

# Convert to uint8
out = 255*(out.astype(np.uint8))

# Step #2 - Hough Transform
lines = cv2.HoughLines(out,1,np.pi/180,60)

# Step #3 - Find points of intersection
pts = []
for i in range(lines.shape[0]):
    (rho1, theta1) = lines[i,0]
    m1 = -1/np.tan(theta1)
    c1 = rho1 / np.sin(theta1)
    for j in range(i+1,lines.shape[0]):
        (rho2, theta2) = lines[j,0]
        m2 = -1 / np.tan(theta2)
        c2 = rho2 / np.sin(theta2)
        if np.abs(m1 - m2) <= 1e-8:
            continue
        x = (c2 - c1) / (m1 - m2)
        y = m1*x + c1
        if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
            pts.append((int(x), int(y)))

# Step #4 - Find convex hull
pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)

# Step #5 - K-Means clustering
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS

# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)

# Step #6 - Find the lengths of each side
centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
    print('Length of side {}: {}'.format(i+1, length))

# Draw the sides of each rectangle in the original image
out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)

# Show the image
cv2.imshow('Output', out5); cv2.waitKey(0); cv2.destroyAllWindows()
</code>```

Body:
So I'm working on an image processing project with sonar images. To be more specific, I am trying to extract the dimensions of an image of a pool taken by a sonar scanner. I was able to extract the rectangular region of the pool but I can not figure out how to get the dimensions of each edge in terms of pixels. I am working with OpenCV in Python.

I would appreciate if anyone would give me any suggestion on how this could be done.

I already tried finding the line intersections of hough lines but that did not have promising results.

Code so far.

import cv2
import numpy as np
from scipy import ndimage as ndi
from scipy.ndimage.measurements import label

def largest_component(indices):
    #this function takes a list of indices denoting
    #the white regions of the image and returns the largest
    #white object of connected indices

    return_arr = np.zeros((512,512), dtype=np.uint8)
    for index in indeces:
        return_arr[index[0]][index[1]] = 255

    return return_arr

image = cv2.imread('sonar_dataset/useful/sonarXY_5.bmp', 0)
image_gaussian = ndi.gaussian_filter(image, 4)
image_gaussian_inv = cv2.bitwise_not(image_gaussian)
kernel = np.ones((3,3),np.uint8)

# double thresholding extracting the sides of the rectangle
ret1, img1 = cv2.threshold(image_gaussian_inv, 120, 255, cv2.THRESH_BINARY)
ret2, img2 = cv2.threshold(image_gaussian_inv, 150, 255, cv2.THRESH_BINARY)

double_threshold = img1 - img2
closing = cv2.morphologyEx(double_threshold, cv2.MORPH_CLOSE, kernel1)

labeled, ncomponents = label(closing, kernel)
indices = np.indices(closing.shape).T[:,:,[1, 0]]
twos = indices[labeled == 2]
area =[np.sum(labeled==val) for val in range(ncomponents+1)]

rectangle = largest_component(twos)

cv2.imshow('rectangle', rectangle)
cv2.waitKey(0)

The original image and extracted object are below.

Body:
So here's what I came up with - it's a bit labour intensive but it does get us to the right answer eventually.  I will be directly using the connected components output that you have shown with the last image.

Use morphological image skeletonization so that we get the skeleton of the blob.  This way, it'll give us the most minimal contour representation such that we get a one pixel wide boundary that goes through the middle of each thick edge.  You can achieve this through Scikit-image's skeletonize method.

Use the Hough Transform which is a line detection method on the skeletonized image.  In summary, it parameterizes lines in the polar domain and the output would be a set of rho and theta that tell us which lines are detected in the skeletonized image.  We can use OpenCV's cv2.HoughLines for that.  It is very important that you do this on the skeletonized image or we will have a lot of candidate lines parallel to where the true delineation of the bounding box is and you wouldn't be able to distinguish between them.

Take each pair of lines and find their point of intersection.  We'd expect that with all pairs of lines, there will be 4 predominant clusters of intersections that give us the corner of each rectangle.

Because of the noise in the contours, we may get more than four points of intersection.  We can use the convex hull to finally get 4 points of intersection for the rectangle.  In summary, the convex hull algorithm operates on a list of points where it defines a subset of points that can minimally encompass the list of points.  We can use cv2.convexHull.

Finally, due to the quantization of the Hough Transform, there may be multiple points that are within the vicinity of each corner.  Therefore, apply K-Means clustering to find 4 clusters of points and thus find their centroids.  We can use cv2.kmeans for that.

Once we find the centroids, we can simply iterate through each pair of points in a cyclical fashion to finally find the distances to each corner and thus find the distances you care about.

Let's step through each point one by one:

Step #1 - Morphological Image Skeletonization

Using Scikit-image's skeletonize, we can skeletonize the connected components image that you've shown above.  Take note that you need to convert the image to binary before you proceed. Once you call the method, we'll need to convert back to unsigned 8-bit integer after for the rest of the process. I've downloaded the image above and saved it locally.  We can run the skeletonize method after:

from skimage.morphology import skeletonize

im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)

# Convert to uint8
out = 255*(out.astype(np.uint8))

We get this image:

Step #2 - Use the Hough Transform

Using the Hough Transform, we can detect the most prominent lines in this image:

lines = cv2.HoughLines(out,1,np.pi/180,60)

Here we specify the search space so that we look for lines where the bin size has a length of 1 and the angles have a bin of 1 degree, or pi / 180 radians.  In summary, the Hough Transform looks at each edge point and iterates through a range of angles theta that are subtended from the origin to each edge point and calculate the corresponding value of rho respecting the bin size.  This pair gets logged into a 2D histogram and we register a vote.  We threshold this 2D histogram so that any bins beyond a certain value are line candidates.  In the above line of code, set the threshold for the bin counts to be 60.

This code is optional, but I wanted to show you what the visualized lines look like:

img_colour = np.dstack([im, im, im])
lines = cv2.HoughLines(edges,1,np.pi/180,60)
for rho,theta in lines[:,0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))
    cv2.line(img_colour,(x1,y1),(x2,y2),(0,0,255),2)

This code I pulled from the following tutorial.  It draws the Hough Transform detected lines in the image as red.  I get the following image:

As we can see, there are four points of intersection in the image.  It's our job next to find these points of intersection.

Step #3 - Find points of intersection

In the Hough Transform, we can relate the length of the line from the origin to a point (x, y) in the image subtended at the angle theta by:

rho = x*cos(theta) + y*sin(theta)

We can also form the equation of the line y = m*x + c in Cartesian form.  We can transform between the two by dividing both sides of the rho equation by sin(theta) then moving the relevant terms to each side:

Therefore, we should cycle through all unique pairs of lines and using the above equation, we can find their point of intersection by setting their Cartesian forms to be equal to each other.  This I won't derive for you for the interest of saving space, but simply set two lines in Cartesian form equal to each other and solve for the x coordinate of intersection.  Once that's done, substitute this point into any of the two lines to find the y coordinate.  We should obviously skip intersection points that go outside of the image in the case of two nearly parallel lines or if we choose two pairs of lines that go in the same direction and don't intersect.

pts = []
for i in range(lines.shape[0]):
    (rho1, theta1) = lines[i,0]
    m1 = -1/np.tan(theta1)
    c1 = rho1 / np.sin(theta1)
    for j in range(i+1,lines.shape[0]):
        (rho2, theta2) = lines[j,0]
        m2 = -1 / np.tan(theta2)
        c2 = rho2 / np.sin(theta2)
        if np.abs(m1 - m2) <= 1e-8:
            continue
        x = (c2 - c1) / (m1 - m2)
        y = m1*x + c1
        if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
            pts.append((int(x), int(y)))

pts is a list of tuples such that we add all points of intersection that are within the image that are not out of bounds.

Step #4 - Use the Convex Hull

We can use this list of tuples and use the convex hull so that we find a list of points that define the outer perimeter of the rectangle. Take note that the order of points defining the rectangle are counter-clockwise. This doesn't matter for this step but it will matter later:

pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)

hull contains a 3D NumPy array that is a subset of the original points of intersection that create the outer boundary of the image.  We can use these points to draw where these are located in the image for illustration

out2 = np.dstack([im, im, im])
for pt in hull[:,0]:
    cv2.circle(out2, tuple(pt), 2, (0, 255, 0), 2)

I've taken the original image and drawn the corner points in green.  We get this image:

Step #5 - Apply K-Means clustering

As you can see in the image above, there are multiple points that map to each corner.  It would be good if we can consolidate the multiple points at each corner into a single point.  One way is to average all of the points in each corner and the easiest way to do that out of box is to use K-Means clustering.  We need the centroids to thus give us the final corner points of the rectangle.  We need to make sure we specify 4 clusters to find.

From the K-Means clustering tutorial from the OpenCV docs, we can use this code:

# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS

# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)

The first parameter is the convex hull of points that need to be in float32 as required by the algorithm.  The second parameter specifies the number of clusters we want to search for, so 4 in our case.  The third parameter you can skip.
 It is a placeholder for the best cluster ID each point is assigned to but we don't need to use it.  criteria are the K-Means parameters used for the mechanics of the algorithm, and the fifth parameter tells us how many attempts we should run to find the best clusters.  We choose 10, meaning that we run K-Means 10 times and choose the clustering configuration that has the least amount of error.  The error is stored in the compactness variable that is output from the algorithm.  Finally, the last variable are optional flags and we set this so that the initial centroids of the algorithm are simply selected at random from the points.

labels provides which cluster ID is assigned to each point and centers is the key variable we need which thus returns:

array([[338.5   , 152.5   ],
       [302.6667, 368.6667],
       [139.    , 340.    ],
       [178.5   , 127.    ]], dtype=float32)

These are the four corner points of the rectangle.  We can see where these line up by drawing them directly on the original image, and we also get this image:

out3 = np.dstack([im, im, im])
for pt in centers:
    cv2.circle(out3, tuple(pt), 2, (0, 255, 0), 2)

Step #6 - Measure the lengths now

Finally, we can cycle through each pair of lines and find the corresponding dimensions.  Take note that because K-Means has the centroids in random order due to the random nature of the algorithm, we can run the convex hull on these centroids to ensure that the order is circular.

centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
    print('Length of side {}: {}'.format(i+1, length))

We thus get:

Length of side 1: 219.11654663085938
Length of side 2: 166.1582489013672
Length of side 3: 216.63160705566406
Length of side 4: 162.019287109375

If you want perspective to see how the bounding box lines up, let's actually draw these lines on the image that are defined at these centres:

out4 = np.dstack([im, im, im])
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    cv2.line(out4, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)

We get:

To see where this lines up with the original image, let's just repeat the code above but drawing the lines on the original image.  I downloaded a copy of the original image to do so:

out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)

For the sake of completeness, here's the entire code from start to finish without all of the debug outputs - we go from reading the image to drawing the lines in the original image with printing the lengths of each side in the detected rectangle.

from skimage.morphology import skeletonize
import cv2
import numpy as np

# Step #1 - Skeletonize
im = cv2.imread('K7ELI.png', 0)
out = skeletonize(im > 0)

# Convert to uint8
out = 255*(out.astype(np.uint8))

# Step #2 - Hough Transform
lines = cv2.HoughLines(out,1,np.pi/180,60)

# Step #3 - Find points of intersection
pts = []
for i in range(lines.shape[0]):
    (rho1, theta1) = lines[i,0]
    m1 = -1/np.tan(theta1)
    c1 = rho1 / np.sin(theta1)
    for j in range(i+1,lines.shape[0]):
        (rho2, theta2) = lines[j,0]
        m2 = -1 / np.tan(theta2)
        c2 = rho2 / np.sin(theta2)
        if np.abs(m1 - m2) <= 1e-8:
            continue
        x = (c2 - c1) / (m1 - m2)
        y = m1*x + c1
        if 0 <= x < img.shape[1] and 0 <= y < img.shape[0]:
            pts.append((int(x), int(y)))

# Step #4 - Find convex hull
pts = np.array(pts)
pts = pts[:,None] # We need to convert to a 3D numpy array with a singleton 2nd dimension
hull = cv2.convexHull(pts)

# Step #5 - K-Means clustering
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS

# Apply KMeans
# The convex hull points need to be float32
z = hull.copy().astype(np.float32)
compactness,labels,centers = cv2.kmeans(z,4,None,criteria,10,flags)

# Step #6 - Find the lengths of each side
centers = cv2.convexHull(centers)[:,0]
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    length = np.sqrt(np.sum((centers[i] - centers[j])**2.0))
    print('Length of side {}: {}'.format(i+1, length))

# Draw the sides of each rectangle in the original image
out5 = cv2.imread('no8BP.png') # Note - grayscale image read in as colour
for (i, j) in zip(range(4), [1, 2, 3, 0]):
    cv2.line(out5, tuple(centers[i]), tuple(centers[j]), (0, 0, 255), 2)

# Show the image
cv2.imshow('Output', out5); cv2.waitKey(0); cv2.destroyAllWindows()


问:
In Mercurial how can I delete a local branch?
How can I delete a local branch in Mercurial using terminal or TortoiseHg?</p>

答:
In Mercurial, the existence of a branch is implied by the existence of commits that are in/on that branch.  Hence to delete a branch, you must strip out all the commits that are in/on that branch.  The simplest way to do that is using hg strip.  See Is there any way to delete local commits in Mercurial?

(Note that there are some tricky conditions when commits within the branch are parents of merges.  It's probably unwise to try to remove a branch that has ever been merged to anything.)


问:
What is the shortcut to focus back on the Atom editor when focus is on the tree-view? And the opposite?
Is there in atom.io a shortcut to get the focus back on the editor (maybe even on a particular pane) when the tree has the focus?

I've checked on the documentation but I couldn't find anything.

答:
On fedora 29 Workstation(Gnome desktop), Linux.

Use the keybinding (keyboard shortcuts) , ALT+\, for toggle between  "Tree View" and Editor area.


问:
is there a way of changing the accent of a region's header according to the value of an item?
I want to set the accent/backgroud-color of a regions's header according to the value of :P4_COLOR item. For example, if :P4_COLOR = blue so the accent of the actual regions's header must be blue (i.e., u-color-1).

I have tried to use html backgroud-color inside an h1 tag, for example:

Title name

but the result is not equal as the template option.

Any ideas?

答:
The following JavaScript would apply the same as the template option

```<code>$('#region_static_id').addClass('t-Region--accent1')
</code>```

Or to use the color modifier

```<code>$('#region_static_id .t-Region-header').addClass('u-color-9-bg')
</code>```

Or you could supply the accent as a substitution string in the Appearance-CSS classes in the region.

Body:
The following JavaScript would apply the same as the template option

$('#region_static_id').addClass('t-Region--accent1')

Or to use the color modifier

$('#region_static_id .t-Region-header').addClass('u-color-9-bg')

Or you could supply the accent as a substitution string in the Appearance-CSS classes in the region.