Find Distance between different coordinates - excel

I have Location data (latitude and longitude) of 1000's of locations and need to compute the distance between each of them taken two combinations at a time.
Example:
Let's just say I have four location data (latitude and longitude data) and want to compute the distance between them
Location Latitude Longitude
1. New York(L1) 40.7128° N 74.0060° W
2. Paris(L2) 48.8566° N 2.3522° E
3. London(L3) 51.5074° N 0.1278° W
4. Moscow(L4) 55.7558° N 37.6173° E
Need to calculate the distance between possible combinations i.e distance between L1&L2, L1&L3, L1&L4, L2&L3, L2&L4 and L3&L4
Excel Formula I'm using to compute distance is
=ACOS(COS(RADIANS(90-Lat1)) *COS(RADIANS(90-Lat2)) +SIN(RADIANS(90-Lat1)) *SIN(RADIANS(90-Lat2)) *COS(RADIANS(Long1-Long2))) *6371
How can I calculate it for large data set say 100's or 1000's of locations?

Alternatively, you can create a VBA function and then loop through your table.
Add this code to a Module in the VBA editor:
Public Function DistBetweenCoord(Lat1 As Double, Long1 As Double, Lat2 As Double, Long2 As Double)
'Cell Formula
'ACOS(COS(RADIANS(90-Lat1)) *COS(RADIANS(90-Lat2)) +SIN(RADIANS(90-Lat1)) *SIN(RADIANS(90-Lat2)) *COS(RADIANS(Long1-Long2))) *6371
With WorksheetFunction
A = Cos(.Radians(90 - Lat1))
B = Cos(.Radians(90 - Lat2))
C = Sin(.Radians(90 - Lat1))
D = Sin(.Radians(90 - Lat2))
E = Cos(.Radians(Long1 - Long2))
DistBetweenCoord = .Acos(A * B + C * D * E) * 6371
End With
End Function
Now you can access this through code or in cell. Here is an example of in-cell:
=DistBetweenCoord(C1,D1,C2,D2)
Here is how to loop through all possible combinations in another Sub. Output is in immediate window.
Sub CalcAllDistances()
With Worksheets("Sheet1")
For i = 1 To 4
For j = i To 4
If i <> j Then
Debug.Print .Cells(i, 2) & " to " & .Cells(j, 2) & ": " & DistBetweenCoord(.Cells(i, 3), .Cells(i, 4), .Cells(j, 3), .Cells(j, 4))
End If
Next j
Next i
End With
End Sub
EDIT - To change output to Sheet2 try the following:
Sub CalcAllDistances()
Dim wks_Output As Worksheet
Set wks_Output = Worksheets("Sheet2")
Dim OutputRow As Long: OutputRow = 1
With Worksheets("Sheet1")
For i = 1 To 4
For j = i To 4
If i <> j Then
wks_Output.Cells(OutputRow, 1).Value = .Cells(i, 2) & " to " & .Cells(j, 2)
wks_Output.Cells(OutputRow, 2).Value = DistBetweenCoord(.Cells(i, 3), .Cells(i, 4), .Cells(j, 3), .Cells(j, 4))
OutputRow = OutputRow + 1
End If
Next j
Next i
End With
End Sub

I would use a matrix.
Create a sheet (like 'GeocodeList' or something) for the geocodes, like your city|lat|lon in the question. Then create a sheet (like 'Distances') for a matrix, where the column and row labels are the city names. Then you can parameter your excel formula using V.LOOKUPs that look up exact codes from GeocodeList.
The formula would look like this (X is row number, Y is column letter.):
=ACOS(COS(RADIANS(90-VLOOKUP($A(X); GEOCODETABLE, LATCOLINDEX, 0)))
*COS(RADIANS(90-VLOOKUP((Y)$1; GEOCODETABLE; LATCOLINDEX, 0)))
+SIN(RADIANS(90-VLOOKUP($A(X); GEOCODETABLE, LATCOLINDEX, 0)))
*SIN(RADIANS(90-VLOOKUP((Y)$1; GEOCODETABLE; LATCOLINDEX, 0)))
*COS(RADIANS(VLOOKUP($A(X); GEOCODETABLE, LATCOLINDEX, 0)-VLOOKUP((Y)$1; GEOCODETABLE; LONCOLINDEX, 0))))
*6371
So basically the VLOOKUP automatically fetches your parameters, and you can extend the formula for the whole matrix.

Related

Excel Matrix Assignment by Referring to a Cell

I am trying to construct a 2x2 matrix dependent on values in some cells (say B1). The code shall take the reference and make some mathematical manipulations, then assign this value to a new cell.
Sub matrix2()
Dim matrix(1 To 2, 1 To 2) As String
k1 = Cells(1, 2).Value
For i = 1 To 2
For j = 1 To 2
k = (-1) ^ (i + j)
matrix(i, j) = "=B1*" & k
Next j
Next i
Range("D1:E2") = matrix
End Sub
In the end, I get what I want but I need to go to each cell and press Enter to convert them in a real value. What I should get here is a matrix dependent on the value in B1. When I change B1, the values in the matrix will automatically change.
Is there any way to make it happen more easily? Because, I will be dealing with 40x40 matrices in the end, and I don't want to go over 1600 cells and press Enter.
I doubt this is going to be helpfull right now, but maybe in the (near) future. With the new MAKEARRAY() function you could do this outside of VBA with relative ease:
Formula in D3:
=MAKEARRAY(2,2,LAMBDA(i,j,B1*(-1^(i+j))))
You have to use a variant-array - not a string-array
Try this:
Sub Matrix2()
Dim arr(1 To 2, 1 To 2) as Variant 'instead of String
k1 = Cells(1, 2).Value
For i = 1 To 2
For j = 1 To 2
k = (-1) ^ (i + j)
arr(i, j) = "=B1*" & k
Next
Next
ActiveSheet.Cells(8, 1).Resize(2, 2).Formula = arr
end sub

Cross reference 2 different columns, then put count of values in another table

I have a worksheet, and I want to be able to go through one column (O) to find the different values, then go to another column (U) and count whether the sting is paper or electronic. Then, I want to be able to take the total of paper/electronic stings from U with each instance in O (source) and put it into the following table on a different sheet with VBA.
Due to the sensitivity of the data, I quickly made a table with basically what I mean. Pretend A is O and B is U.
And I want the output in this table, or if there is a better way to present the data:
I've tried making a pivot table, but it simply counts each instance of the paper/electronic string in the sheet, and I need to cross reference the values in O with U.
Here is the formula what you desire. Remember that we need to change source value and Fillining medium value in each row. you can see from the image that in formula for Source A values are "A" and "Paper" for paper count and "A" and "Electronic" for electronic count. the formulas for Source A are written at the bottom of the table and formula for Source C you can See from formula Bar. This is to show you the change you need to make in formula for each source.
if you have excel 365 you can just use the unique/countifs function. For simplicity I assume your data is in col A & B
To get the unique values (source) col E:
=UNIQUE(A:A)
To count (manually add "paper" as header in col F:
=COUNTIFS(A:A;E2;B:B;$F$1)
Do the same for the other values.
EDIT:
Anything can be done in code:
Option Explicit
Sub DictUniqueFinal()
Dim arr, arr2, arrH, j As Long, dict As Object, id As String
'setup some arrays
arrH = Split("Source, Paper, Electronic", ",")
arr = Sheet1.Range("A1").CurrentRegion.Offset(1, 0).Value2 'load source without headers
ReDim arr2(1 To UBound(arr), 1 To 3)
'setup the dict
Set dict = CreateObject("Scripting.Dictionary") 'create dictionary lateB
For j = 1 To UBound(arr) - 1 'traverse source
id = arr(j, 1)
If Not dict.Exists(id) Then 'create key
If arr(j, 2) = "paper" Then
dict.Add id, 1 & "," & 0
Else
dict.Add id, 0 & "," & 1
End If
Else 'update key
If arr(j, 2) = "paper" Then
dict(id) = Split(dict(id), ",")(0) + 1 & "," & Split(dict(id), ",")(1)
Else
dict(id) = Split(dict(id), ",")(0) & "," & Split(dict(id), ",")(1) + 1
End If
End If
Next j
'build final array
ReDim arr2(0 To dict.Count - 1, 1 To 3)
For j = 0 To dict.Count - 1
arr2(j, 1) = dict.Keys()(j)
arr2(j, 2) = Split(dict.Items()(j), ",")(0)
arr2(j, 3) = Split(dict.Items()(j), ",")(1)
Next j
'dump to sheet
With Sheet2
.Range(.Cells(1, 1), .Cells(1, UBound(arrH) + 1)).Value2 = arrH
.Range(.Cells(2, 1), .Cells(UBound(arr2) + 2, UBound(arr2, 2))).Value2 = arr2
End With
End Sub
It's a bit long and I had to hard code, but I found a solution, thanks to #AnmolKumar I looked in to Countif and found this:
ws2.Range("F15").Value2 = Excel.WorksheetFunction.CountIfs, _
(ws3.Range("O1:O" & lstRow2), "A", ws3.Range("U1:U" & lstRow2), "Paper")
I'll just have to do it for each different section

Find the cell that is closest to a certain value in Excel

In Excel, I have many products with different sizes listed in columns, such that the sizes "10x10 cm", "11x11 cm" and "15x15 cm" belongs to Product A, etc.
In some other cells, I am selecting a product (either Product A, Product B, or Product C) and a size.
I want, for each of the other products, to determine which size is closest to the selected product:
I don't know how to solve this. One solution might be to remove all non-numeric characters from the strings and add the two values on each side of the "x" and then select the size with the lowest absolute difference from the sum of the selected size.
But I guess it would be easier to do a mapping and use a VLOOKUP to choose the first found size in a given column.
However, the problem is that I do not only have 3 products with a few different sizes, but rather 15 different products with 10 different sizes, so I don't know how to do a mapping in a clever way.
1) Creating a lookup table with the values extracted for each product,
Source sheet:
Code:
Sub lookup()
Dim i As Long, j As Long, prod As Integer, str As String
prod = InputBox("Enter Number of Products")
Sheets.Add.Name = "LookupSheet"
j = 1
For i = 1 To prod
Columns(i).Copy Sheets("LookupSheet").Cells(1, j)
j = j + 2
Next i
For j = 1 To prod * 2 Step 2
For i = 2 To Sheets("LookupSheet").Cells(Rows.Count, j).End(xlUp).Row
str = Replace(Replace(Sheets("LookupSheet").Cells(i, j), " ", ""), "cm", "")
Sheets("LookupSheet").Cells(i, j + 1) = Left(str, InStr(str, "x") - 1) _
* Mid(str, InStr(str, "x") + 1, 999)
Next i
Next j
End Sub
This simple code creates a lookup sheet with the corresponding values. The code ignores any spaces present between the texts.
LookupSheet:
Since you have 15 different products, run this macro to extract the lookup data. This should be a one time activity unless you have additional products.
2) Assuming you enter the product and dimensions to F5 and F6, i would suggest you to data validation with dropdowns to select from the list,
3) Using a worksheet_change event, detect for changes in F5 and F6,
Code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim str As String, result As Integer, i As Long
'F5 and F6 contains Product and Size repectively
If (Target.Address = "$F$5" Or Target.Address = "$F$6") _
And Range("F5") <> "" And Range("F6") <> "" Then
str = Replace(Replace(Range("F6"), " ", ""), "cm", "")
result = Left(str, InStr(str, "x") - 1) * Mid(str, InStr(str, "x") + 1, 999)
j = 8
For i = 1 To Cells(1, Columns.Count).End(xlToLeft).Column
If Cells(1, i) <> Range("F5") Then
Range("E" & j) = Cells(1, i)
j = j + 1
End If
Next i
End If
End Sub
This code automatically populated the rest of the product types in the column E,
4) The variable result would contain the product/area of the value that you provide in F6. The only task pending would be to loop through the lookup sheet to find the nearest match. The Algorithm is below,
Algorithm:
Compare cell F5 with the data in row 1 of lookup sheet (need to loop)
If they are equal, ignore and move to next value. If not, need to loop the immediate next column to find the next match, and populate the result in the corresponding cell in source sheet.
Algorithm for column wise looping is below,
Steps:
diff = cell.value - result
if diff < 0 then multiply diff by -1
loop:
nextdiff = nextcell.value - result (multiply by -1 if negative)
if nextdiff < diff then
diff = nextdiff
end if
end loop:
The cell value with the least difference would be your best match for that particular product type.
Bit lengthier explanation, hope this helps.

How to implement an advanced look up macro in excel?

How do I have to change the following macro code and formula which works for the data and question below(earlier data/question,column F) to make it suitable for Problemstatement (2) and (3) ?
(1)
Earlier data
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
Intention of the column F
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN) and is >0.
** Macro code and formula for column F**
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC())
(2)
How do I have to change the macro and formula to archieve the same in the following data format:
New data 1
Colums AZ,BA,BB,BC,BD,BE,BF,BG
Row 1 4,4,4,10,10,10,120,444
Row 2 4,4,4,12,12,12,144,444
Row 3 4,4,4,14,14,14,168,444
Row 4 3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111
Row 6 1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,???
Intention of the column BG
If(And(AZ>0;BA>0;BB>0);Concatenate(AZ;BA;BB))
otherwise, the adress cell of Concatenate (AZ;BA;BB), unequal to 000, to minimize the following difference (AZn*BCx+BAn*BDx+BBn*BEx)-BFn
(3)
How do I have to change the macro and formula from (1) to archieve the same in the following data format:
New data 2
Colums AZ,BA,BB,BC,BD,BE,BF,BG,BH,BI,BJ,BK,BL,BM,BN
Row 1 4,4,4,10,10,10,120,444,3,3,3,10,10,10,90,333
Row 2 4,4,4,12,12,12,144,444,3,3,3,12,12,12,108,333
Row 3 4,4,4,14,14,14,168,444,3,3,3,14,14,14,126,333
Row 4 3,3,3,16,16,16,144,333,3,3,3,16,16,16,144,333
Row 5 1,1,1,18,18,18,54,111,2,2,2,18,18,18,108,222
Row 6 1,1,1,20,20,20,60,111,1,1,1,20,20,20,60,111
Row 7 0,0,0,22,22,22,0,?,0,0,0,22,22,22,0,?
Intention of the column BN
If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0
otherwise the adress of the cell either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) to minimize (AZn*BCx+BAn*BDx+BBn*BEx)-BFn or (BHn*BKx+BIn*BLx+BJn*BMx)-BNn .In this case i need to find the adress of either concatenate (AZ;BA;BB) or concatenate (BH;BI;BJ) with the min difference and that cell of BN (concatenate) unequal to 000.
I appriciate the help. Thank you very much!!!!
To solve problem 2, the macro is basically identical. You just need to replace the C range with the AZ range, and change the calculation of tmp:
Public Function CalcBG()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
tmp = ((c.Value * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -2)) - c.Offset(0, 6).Value2) 'This is your calculation (AZn * BCx) + ... - BFn
If tmp < minimum Then
minimum = tmp
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBG = rngMin.Address 'Return the address rather than the value
End Function
And the formula:
=IF(AND(AZ1>0,BA1>0,BB1>0),CONCATENATE(AZ1,BA1,BB1),CalcBG())
Place that in BG1 and copy down Column BG.
Problem 3
This added a couple more variables, but the basic structure is the same.
Based on the data, I also assumed that "If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0);0" should have been If(And(AZ>0;BA>0;BB>0;BH>0;BI>0;BJ>0),concatenate(bh,bi,bj). Otherwise your values in BO would all be 0. Also, your last column is BO, not BN. :)
Here's the macro:
Public Function CalcBO()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Debug.Print rngCurrent.Address
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp1 As Long
Dim tmp2 As Long
Dim lowest As Long
Dim rngAZ As Range
Set rngAZ = ActiveSheet.Range("AZ1:AZ" & rngCurrent.Row - 1)
Debug.Print rngAZ.Address
For Each c In rngAZ.Cells
If c.Value2 <> 0 Then
'(AZn*BCx+BAn*BDx+BBn*BEx)-BFn
tmp1 = ((c.Value2 * rngCurrent.Offset(0, -12).Value2) + (c.Offset(0, 1).Value2 * rngCurrent.Offset(0, -13).Value2) + (c.Offset(0, 2).Value2 * rngCurrent.Offset(0, -10).Value2)) - c.Offset(0, 6).Value2
'(BHn*BKx+BIn*BLx+BJn*BMx)-BNn
tmp2 = ((c.Offset(0, 8).Value2 * rngCurrent.Offset(0, -4).Value2) + (c.Offset(0, 9).Value2 * rngCurrent.Offset(0, -3).Value2) + (c.Offset(0, 10).Value2 * rngCurrent.Offset(0, -2).Value2)) - c.Offset(0, 14).Value2
lowest = WorksheetFunction.Min(tmp1, tmp2)
If lowest < minimum Then
minimum = lowest
Debug.Print minimum
Set rngMin = c.Offset(0, 7)
Debug.Print rngMin
End If
End If
Next c
Debug.Print minimum
Debug.Print rngMin.Address
CalcBO = rngMin.Address
End Function
And here is the formula for BO1. Copy down as usual:
=IF(AND(AZ1>0,BA1>0,BB1>0,BH1>0,BI1>0,BJ1>0),CONCATENATE(BH1,BI1,BJ1),CalcBO())
There's a lot of repetition among those macros, and they could probably be condensed. But they should do what you need.
One note: The macros won't differentiate between equal minimum values. For example, in problem set 3, the two calculations (AZn ... - BFn and BHn ... - BNn) return the same value, 6, which also happens to be the minimum value among all the calculations. In this case, it returns the first address (AZn ... - BFn). So in Problem 3, you will get the answer $BG$6 in BO7, even though $BG$6 and $BO$6 both satisfy the conditions. Your requirements didn't specify what to do in case there was more than one minimum value, so I left it as it is.
Let me know if you need any help with the above.

Merge the cell values if duplicates exist

I want to merge the values in column B if Duplicates exist in Column A
A B
123 A
123 B
123 C
456 D
456 E
789 F
My output should look like this
A B
123 A B C
456 D E
789 F
I have a large amount of data and it is hard to do it manually ,So u guys have any idea to do it in macros in Excel?
Any help will be appreciated..Thanks in Advance
I'd cheat, and use formulas as follows;
1) Sort by column A
2) In col C, add a formula to test if the current one is last (assuming there's a header, put this in C2
=if(A2<>A3,TRUE,FALSE)
Now, this should only be true for the last cell in a series of same ID's
3) in Col D, add a formula for concatenating if the ID's are the same,
=if(A2=A1,D1&" "&B2,B2)
4) Filter on column C to show only the last cell in each series.
Cheers.
In case you want the resultant data in the same cells the original data existed ie not in Cell 10, then you have to store the source data in a two dimensional array. Then from the array we have use the above code to insert the data in the same place the original data existed. Here goes the listing to accomplish the task:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim names(2 To 7, 2)
For i = 2 To 7
names(i, 1) = Cells(i, 1)
names(i, 2) = Cells(i, 2)
Next
On Error Resume Next:
Sheet1.Cells.Clear
cnt = 2
For i = 2 To 7
strg = strg + names(i, 2)
If names(i + 1, 1) <> names(i, 1) Then
Cells(cnt, 1) = names(i, 1)
Cells(cnt, 2) = strg
cnt = cnt + 1
strg = ""
End If
Next
End Sub
Please note that I have declared names array with two dimesnions to store the data. Then the array is searched to get the result.
You can use the following macro:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
cnt = 10
For i = 2 To 7
strg = strg + Cells(i, 2)
If Cells(i + 1, 1) <> Cells(i, 1) Then
Cells(cnt, 1) = Cells(i, 1)
Cells(cnt, 2) = strg
cnt = cnt + 1
strg = ""
End If
Next
End Sub
The requested data will be printed from Cells 10

Resources