To Build or Not To Build: Determining a Quantitative Metric for Land Planning and Allocation

Authors

  • Zixuan Li
  • Yan Xiao
  • Ziang Li
  • Jocelyn Wang

DOI:

https://doi.org/10.61173/1chhk205

Keywords:

Linear Programming, TOPSIS, Genetic Algorithm, Cobb-Douglas Function, Differential

Abstract

Land planning is crucial to ensure that urban development occurs with consideration to the economic, social, and 
environmental interests of a community. Many conflicting factors must often be considered to adhere to optimal land 
planning. In this paper, our team makes a quantitative decision metric that can analyze these factors and determine the 
“best” choice from a given set of development options and the allocation of those choices. First, linear programming is 
used to determine two “best” development options: one that maximizes both economic and social factors and one that 
minimizes negative environmental factors while maximizing social. The maxima and minima from linear programming 
are then applied to the Technique for Order of Preference by Similarity to the Ideal Solution to obtain a third real-world 
“overall best” option that balances economic and environmental factors with a desired weighting. A genetic algorithm 
is then used to determine the optimal positioning of the three established “bests” by analyzing opportunity costs based 
on an environmental degradation penalty index. Finally, the Cobb-Douglas Function is used to conduct a short- and 
long-term analysis of each result’s profit by solving differential equations about inflation. This model is then applied 
to the parcel of land in Victory, NY, using data obtained from research. The ideal option and positioning are found to 
be 267 acres of a sports complex in the northern half of the land, 129 acres of regenerative farm directly west of the 
sports complex, 344 acres of a solar array in the southernmost region of the land, and 1 acre of agritourism center on the 
eastern side of the land. Conducting a sensitivity analysis on our model reveals that the linear programming results are 
most affected by the area and societal benefit restrictions but that the TOPSIS results remain relatively stable regardless 
of the changing parameters. Our model is adjusted to account for Micron Technology, Inc. building a nearby fabrication 
facility. As this facility brings more jobs and thus more people, the profit of facilities that involve tourism will increase. 
However, nature-based facilities will suffer detriment due to pollution caused by the facility. With these adjustments, 
the model is re-run, and the results are compared to the previous results. In this scenario, there would be a greater area 
of the solar array and agritourist center, a smaller sports complex, no regenerative farm, and 128 acres of ranch. Finally, 
the generalizability of our model is discussed by first discussing its application in Shenzhen, China, and then widening 
the scope to any location in any country. Our model will provide the most implementable results in rural environments 
due to its quantitative nature that cannot consider complicated urban planning laws but that the model can be applied to 
nearly any scenario as long as data is provided.

References

[1] Richard Battye, Bjoern Garbrecht, and Adam Moss. Tight constraints on f-and d-term hybrid inflation scenarios. Physical Review D, 81(12):123512, 2010.

[2] Majid Behzadian, S Khanmohammadi Otaghsara, Morteza Yazdani, and Joshua Ignatius. A state-of-the-art survey of topics applications. Expert Systems with applications, 39(17):13051– 13069, 2012.

[3] Edward J Blakely. Urban planning for climate change. 2007.

[4] H Christopher Frey and Sumeet R Patil. Identification and review of sensitivity analysis methods. Risk analysis, 22(3):553– 578, 2002.

[5] Wikimedia Commons. File:linear optimization in a 2-dimensional polytope.svg — wikimedia commons, the free media repository, 2022. [Online; accessed April-2023].

[6] Juan Angel Demerutis and Magdalena Vicu´ na. Urban and regional planning in Latin America and the Caribbean. The Routledge Handbook of Urban Studies in Latin America and the Caribbean, pages 357–382, 2023.

[7] Jean Dreze and Nicholas Stern.` Policy reform, shadow prices, and market prices. Journal of public economics, 42(1):1– 45, 1990.

[8] Tomas J Havr’ anek.’ Cobb–douglas production function. In Dictionary of Ecological Economics, pages 71–71. Edward Elgar Publishing, 2023.

[9] Seyedali Mirjalili and Seyedali Mirjalili. Genetic algorithm. Evolutionary Algorithms and Neural Networks: Theory and Applications, pages 43–55, 2019.

[10] Stephen Palmer and James Raftery. Opportunity cost. Bmj, 318(7197):1551–1552, 1999.

[11] Jason Papathanasiou, Nikolaos Ploskas, Jason Papathanasiou, and Nikolaos Ploskas. Topsis. Multiple Criteria Decision Aid: Methods, Examples and Python Implementations, pages 1–30, 2018.

[12] Jianfa Shen. Urban growth and sustainable development in Shenzhen city 1980-2006. Open Environmental Sciences Journal, 2(1), 2008.

[13] Robert J Vanderbei et al. Linear programming. Springer, 2020. Appendix A Substantiating Tables and Data Table 14 Environmental Degradation Penalties Wetland Developed Forest Crop Biodiversity 8 1 9 2 Soil Erosion 7 0 10 2 Pollution 9 0 8 1 Total 24 1 27 5 E Factor 6 29 3 25 Table 15 Developed Coordi-nates lower-left vertex higher-right vertex (0, 3.5) (3.5, 10) (5, 0) (9, 4) (4.5, 7) (6, 8.5) (7.5, 7) (11, 10.5) (4, 12.5) (5, 15) (14.5, 0) (19, 5) (17.5, 8) (19, 10) Table 16 Crop Land Coordinates lower-left vertex higher-right vertex (3.5, 1.5) (5, 4) (11.5, 0.5) (12.5, 2) (0, 14) (0.5, 15) (5.5, 13.5) (7, 15) (19, 7) (20, 11) (16, 15) (17, 16.5) Table 17 Wetland Coordinates lower-left vertex higher-right vertex (3.5, 4) (5, 6.5) (8, 12) (11.5, 18.5) Dean&Francis Appendix B All learning Curves and Final DNA of Genetic Algorithm Table 18 Further DNA-related Data of the TOPSIS Overall Best option with 0.4 - 0.6 Occupation Rate Dean&Francis Table 19 Further DNA-related Data of the Economic best option with 0.4 - 0.6 Occupation Rate Dean&Francis Table 20 Further DNA-related Data of the Environmental best option with 0.4 - 0.6 Occupation Rate Dean&Francis Table 21 Further DNA-related Data of different best option with 0.5 Occupation Rate with the addition of factory Appendix C Genetic Algorithm Code Table 22 Appendix Code of GA Processes GA Process Code Lines Objective Function [84~224] Conversion [230~244] Natural Selection [247~254] Reproduction [257~262] Mutation [265~269] """ Visualize Genetic Algorithm to find a maximum point in a function. """ import numpy as np import matplotlib.pyplot as plt DNA_SIZE = 5 * 3 * 4 # DNA length POP_SIZE = 1000 # population size CROSS_RATE = 0.8 # mating probability (DNA crossover) MUTATION_RATE = 0.002 # mutation probability N_GENERATIONS = 400 length_bound = [1, 20] # x upper and lower bounds x_bound = [0, 20] y_bound = [0, 17.5] # Python program to find total area of two # overlapping Rectangles # Returns Total Area of two overlap Dean&Francis # rectangles def binary_to_decimal(binary_string): decimal = 0 for i in range(len(binary_string)): digit = int(binary_string[i]) power = 4 - i decimal += digit * (2 ** power) return decimal def overlappingArea(l1, r1, l2, r2): x = 0 y = 1 ’’’ Length of intersecting part i.e start from max(l1[x], l2[x]) of x-coordinate and end at min(r1[x], r2[x]) x-coordinate by subtracting start from end we get required lengths ’’’ x_dist = (min(r1[x], r2[x]) max(l1[x], l2[x])) y_dist = (min(r1[y], r2[y]) max(l1[y], l2[y])) areaI = 0 if x_dist > 0 and y_dist > 0: areaI = x_dist * y_dist return areaI #all environmental factors Ew = 30 - 24 Ed = 30 - 1 Ef = 30 - 27 Ec = 30 - 5 osc_area = 267 * 0.6 / 2.709 rf_area = 129 * 0.6 / 2.709 sa_area = 344 * 0.6 / 2.709 ac_area = 1 * 0.6 / 2.709 CRO = [[[4, 12.5], [5, 15]], [[17.5, 8], [19, 10]], [[7.5, 7], [11, 10.5]], [[4.5, 7], [6, 8.5]], [[0, 3.5], [3.5, 10]], [[14.5, 0], [19, 5]], [[5, 0], [9, 4]]] dev = [[[16, 15], [17, 16.5]], [[0, 14], [0.5, 15]], [[5.5, 13.5], [7,15]], [[19, 7], [20, 11]], [[3.5, 1.5], [5, 4]], [[11.5, 0.5], [12.5, 2]]] wet = [[[8, 12], [11.5, 18.5]], [[3.5, 4], [5, 6.5]]] squareconstrains = [[[0, 15], [8, 17.5]], [[0, 17.5], [20, 1000]], [[20, 0], [1000, 17.5]]] def F(osc_x, rf_x, sa_x, ac_x, osc_y, rf_y, sa_y, ac_y, osc_width, rf_width, sa_width, ac_width): global osc_area, rf_area, sa_area, ac_area, dev, CRO, wet, Ew, Ec, Ef, Ed total = 0 if(osc_width == 0): return 0 if (rf_width == 0): return 0 if (sa_width == 0): return 0 if (ac_width == 0): return 0 osc_length = osc_area / osc_width rf_length = rf_area / rf_width sa_length = sa_area / sa_width ac_length = ac_area / ac_width if(osc_y < -8.5/6 * osc_x + 8.5): return 0 if (rf_y < -8.5 / 6 * rf_x + 8.5): return 0 if (ac_y < -8.5 / 6 * ac_x + 8.5): return 0 if (sa_y < -8.5 / 6 * sa_x + 8.5): return 0 if (osc_y + osc_length > -7.5 / 4.5 * (osc_x + osc_width) + 44.33333): Dean&Francis return 0 if (rf_y + rf_length > -7.5 / 4.5 * (rf_x + rf_width) + 44.33333): return 0 if (ac_y + ac_length > -7.5 / 4.5 * (ac_x + ac_width) + 44.33333): return 0 if (sa_y + sa_length > -7.5 / 4.5 * (sa_x + sa_width) + 44.33333): return 0 osc = [[osc_x, osc_y], [osc_x + osc_width, osc_y + osc_length]] rf = [[rf_x, rf_y], [rf_x + rf_width, rf_y + rf_ length]] sa = [[sa_x, sa_y], [sa_x + sa_width, sa_y + sa_length]] ac = [[ac_x, ac_y], [ac_x + ac_width, ac_y + ac_length]] #Outdoor Sport Complex area = osc_area #developed for i in range(0, 6): total += Ed * overlappingArea(osc[0], osc[1], dev[i][0], dev[i][1]) area -= overlappingArea(osc[0], osc[1], dev[i][0], dev[i][1]) area -= overlappingArea(osc[0], osc[1], dev[i][0], dev[i][1]) #Crop for i in range(0, 7): total += Ed * overlappingArea(osc[0], osc[1], cro[i][0], cro[i][1]) area -= overlappingArea(osc[0], osc[1], cro[i][0], cro[i][1]) #Wetland for i in range(0, 2): total += Ed * overlappingArea(osc[0], osc[1], wet[i][0], wet[i][1]) area -= overlappingArea(osc[0], osc[1], wet[i][0], wet[i][1]) #taking off the part that is not in the map for j in range(0, 3): if (overlappingArea(osc[0], osc[1], squareconstrains[j][0], squareconstrains[j][1]) > 0): return 0 total += area * Ef # Regenetive farm area = rf_area # developed for i in range(0, 6): total += Ed * overlappingArea(rf[0], rf[1], dev[i][0], dev[i][1]) area -= overlappingArea(rf[0], rf[1], dev[i][0], dev[i][1]) # Crop for i in range(0, 7): total += Ec * overlappingArea(rf[0], rf[1], cro[i][0], cro[i][1]) area -= overlappingArea(rf[0], rf[1], cro[i][0], cro[i][1]) # Wetland for i in range(0, 2): total += Ew * overlappingArea(rf[0], rf[1], wet[i][0], wet[i][1]) area -= overlappingArea(rf[0], rf[1], wet[i][0], wet[i][1]) # taking off the part that is not in the map for j in range(0, 3): if (overlappingArea(rf[0], rf[1], squareconstrains[j][0], squareconstrains[j][1]) > 0): return 0 total += area * Ef #Solar array area = sa_area # developed for i in range(0, 6): total += Ed * overlappingArea(sa[0], sa[1], dev[i][0], dev[i][1]) area -= overlappingArea(sa[0], sa[1], dev[i][0], dev[i][1]) # Crop for i in range(0, 7): total += Ec * overlappingArea(sa[0], sa[1], cro[i][0], cro[i][1]) area -= overlappingArea(sa[0], sa[1], cro[i][0], cro[i][1]) # Wetland for i in range(0, 2): total += Ew * overlappingArea(sa[0], sa[1], wet[i][0], wet[i][1]) area -= overlappingArea(sa[0], sa[1], wet[i][0], wet[i][1]) # taking off the part that is not in the map for j in range(0, 3): if (overlappingArea(sa[0], sa[1], squareconstrains[j][0], squareconstrains[j][1]) > 0): Dean&Francis return 0 total += area * Ef # agriculture center area = ac_area # developed for i in range(0, 6): total += Ed * overlappingArea(ac[0], ac[1], dev[i][0], dev[i][1]) area -= overlappingArea(ac[0], ac[1], dev[i][0], dev[i][1]) # Crop for i in range(0, 7): total += Ec * overlappingArea(ac[0], ac[1], cro[i][0], cro[i][1]) area -= overlappingArea(ac[0], ac[1], cro[i][0], cro[i][1]) # Wetland for i in range(0, 2): total += Ew * overlappingArea(ac[0], ac[1], wet[i][0], wet[i][1]) area -= overlappingArea(ac[0], ac[1], wet[i]

[0] , wet[i][1]) # taking off the part that is not in the map for j in range(0, 3): if (overlappingArea(ac[0], ac[1], squareconstrains[j][0], squareconstrains[j][1]) > 0): return 0 total += area * Ef #check if 4 rectangles touch each other if(overlappingArea(ac[0], ac[1], sa[0], sa[1]) > 0): return 0 if (overlappingArea(osc[0], osc[1], sa[0], sa[1]) > 0): return 0 if (overlappingArea(rf[0], rf[1], sa[0], sa[1]) > 0): return 0 if (overlappingArea(rf[0], rf[1], ac[0], ac[1]) > 0): return 0 if (overlappingArea(rf[0], rf[1], osc[0], osc[1]) > 0): return 0 if (overlappingArea(osc[0], osc[1], ac[0], ac[1]) > 0): return 0 return total # find non-zero fitness for selection def get_fitness(pred): return pred # convert binary DNA to decimal and normalize it to a range(0, 5) def translateDNA(pop): newpop = [] for i in range(0, POP_SIZE): newpop.append([]) for i in range(0, POP_SIZE): for j in range(0, DNA_SIZE//3, 5): string = str(pop[i][j]) + str(pop[i][j+1]) + str(pop[i][j+2]) + str(pop[i][j+3]) + str(pop[i][j+4]) newpop[i].append(binary_to_decimal(string) / float(2**5-1) * x_bound[1]) for j in range(DNA_SIZE//3, DNA_SIZE//3 * 2, 5): string = str(pop[i][j]) + str(pop[i][j+1]) + str(pop[i][j+2]) + str(pop[i][j+3]) + str(pop[i][j+4]) newpop[i].append(binary_to_decimal(string) / float(2**5-1) * y_bound[1]) for j in range(DNA_SIZE//3 * 2, DNA_SIZE, 5): string = str(pop[i][j]) + str(pop[i][j+1]) + str(pop[i][j+2]) + str(pop[i][j+3]) + str(pop[i][j+4]) newpop[i].append(binary_to_decimal(string) / float(2**5-1) * length_bound[1]) return newpop def select(pop, fitness): # nature selection wrt pop’s fitness if(fitness.sum() == 0): pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) return pop else: IDX = np.random.choice(np.arrange(POP_SIZE), size=POP_SIZE, replace=True, p=fitness/(fitness.sum())) return pop[idx] def crossover(parent, pop): # mating process (genes crossover) if np.random.rand() < CROSS_RATE: i_ = np.random.randint(0, POP_SIZE, size=1) # select another individual from pop cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(bool) # choose crossover points parent[cross_ Dean&Francis points] = pop[i_, cross_points] # mating and produce one child return parent def mutate(child): for point in range(DNA_SIZE): if np.random.rand() < MUTATION_RATE: child[point] = 1 if child[point] == 0 else 0 return child pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) # initialize the pop DNA new_row = np.array([1 ,0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1 ,1, 1, 1, 0, 1,1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0 ,0, 0, 0, 0, 0, 1]) pop[0] = new_row a = [] b = [] for _ in range(N_GENERATIONS): a.append(_) print(“This is generation ->”, _) F_values = [] list2 = translateDNA(pop) for i in range(0, POP_SIZE): list1 = list2[i] F_values.append(F(list1[0], list1[1], list1[2], list1[3], list1[4], list1[5], list1[6], list1[7], list1[8], list1[9], list1[10], list1[11])) # compute function value by extracting DNA F_values = np.asarray(F_values) print(F_values, “F_values”) fitness = get_fitness(F_values) b.append(max(fitness)) print(max(fitness)) print(“Most fitted DNA: “, pop[np.argmax(fitness), :]) pop = select(pop, fitness) pop_copy = pop.copy() for parent in pop: child = crossover(parent, pop_copy) child = mutate(child) parent[:] = child # parent is replaced by its child plt.scatter(a, b) plt.show() Appendix D Genetic Algorithm DNA Graph Code string = “1 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 0 1 1 0 1 0 1 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 0 1 1” import turtle import random # setting up of all the turtles, game chart and screen screen = turtle.Screen() screenW = 1280 screenH = 720 pen = turtle. Turtle() osc_area = 267 * 0.6 / 2.709 rf_area = 129 * 0.6 / 2.709 sa_area = 344 * 0.6 / 2.709 ac_area = 1 * 0.6 / 2.709 DNA_SIZE = 5 * 3 * 4 # DNA length POP_SIZE = 400 # population size CROSS_RATE = 0.8 # mating probability (DNA crossover) MUTATION_RATE = 0.003 # mutation probability N_GENERATIONS = 1000 length_bound = [1, 20] # x upper and lower bounds x_bound = [0, 20] y_bound = [0, 17.5] area = [osc_area, rf_area, sa_area, ac_area] def drawers(l1, l2, r1, r2): pen.penup() pen.goto(l1, l2) pen.pendown() pen.goto(l1, r2) pen.goto(r1, r2) pen.goto(r1, l2) pen.goto(l1, l2) newstring = “” for x in range(0, 119, 2): newstring += string[x] def binary_to_decimal(binary_string): decimal = 0 for i in range(len(binary_string)): digit = int(binary_string[i]) power = 4 - i decimal += digit * (2 ** power) return decimal def translateDNA(given): newpop = [[],[],[],[]] for j in range(0, DNA_SIZE//3, 5): string = str(given[j]) + str(given[j + 1]) + str(given[j + 2]) + str(given[j+3]) + str(given[j+4]) Dean&Francis newpop[(j) // 5].append(binary_to_decimal(string) / float(2**5-1) * x_bound[1]) for j in range(DNA_SIZE//3, DNA_SIZE//3 * 2, 5): string = str(given[j]) + str(given[j+1]) + str(given[j+2]) + str(given[j+3]) + str(given[j+4]) newpop[(j-20) // 5].append(binary_to_decimal(string) / float(2**5-1) * y_bound[1]) for j in range(DNA_SIZE//3 * 2, DNA_SIZE, 5): string = str(given[j]) + str(given[j+1]) + str(given[j+2]) + str(given[j+3]) + str(given[j+4]) newpop[(j-40) // 5].append(binary_to_decimal(string) / float(2**5-1) * length_bound[1]) return newpop array = translateDNA(newstring) print(array) x =10 pen.goto(6 * x, 0) pen.goto(0, 8 * x) pen.goto(0, 15 * x) pen.goto(8 * x, 15 * x) pen.goto(8 * x, 17.5 * x) pen.goto(15.5 * x, 17.5 * x) pen.goto(20 * x, 11 * x) pen.goto(20 * x, 0 * x) pen. goto(6 * x, 0) y = x for x in range(0, 4): drawrec(y * array[x][0], y * array[x][1], y *(array[x][0] + array[x][2]), y*(array[x][1] + area[x] / array[x][2])) turtle.done() Appendix E Linear Programming Code % Create optimization variables b = optimvar(“b”,1,8,”LowerBound”,0); % Set initial starting point for the solver initialPoint.b = zeros(size(b)); % Create problem problem = optimproblem(“ObjectiveSense”,”Maximize”); % Define problem objective problem.Objective = 0.05 * (-4.86) * (200 * b(1) +50*b(2) -25*b(3)+500*b(4)-20*b(5)-3330*b(6)-2*b(7)-2030*b(8)) + 0.95 * 2 * (13557.69* b(1) +382.43*b(2) + 161.35* b(3)+ 509.26*b(4) + 287.37*b(5)+ 21070.95*b(6)+3999.91*b(7)+193.58*b(8)); % Define problem constraints problem.Constraints.constraint1 = sum(b) <= 741; problem.Constraints.constraint2 = 3 * b(1) +4*b(2) +8*b(3)+9*b(4)+9*b(5)+3*b(6)+6*b(7)+7*b(8) >= 3000; problem.Constraints.constraint3 = 10 * b(1) +9*b(2) +b(3)+2*b(4)+2*b(5)+b(6)+9*b(7)+2*b(8) >= 2000; problem.Constraints.constraint4 = 9.5* b(1) +b(2) +7.5 * b(3)+ 8.5*b(4)+8.5*b(5)+2.5 * b(6)+9*b(7)+7.5*b(8) >= 4500; problem.Constraints.constraint5 = 9.5* b(1) +8 * b(2) +b(3)+ b(4)+1.5*b(5)+ b(6)+7*b(7)+b(8) >= 1500; % Display problem information show(problem); % Solve problem [solution,objectiveValue,reasonSolverStopped] = solve(problem,initialPoint); % Display results solution reasonSolverStopped objectiveValue % Remove Variable clearvars b initialPoint reasonSolverStopped objectiveValue Appendix F TOPSIS Code #include #include using namespace std; int main(){ double pr; double eco; double array1[8]; double bd, wd; double array[8][8] = { {123,0,0,128,0,487,3,0}, {0,0,0,0,0,510,147,84}, {0,0,0,0,54,536,151,0}, {0,0,0,0,2,511,147,81}, {0,0,0,0,0,510,147,84} }; for(int i = 0; i < 5;i ++){ pr = 13557.69 * array[i][0] + 382.43 * array[i][1] + 161.35 * array[i][2] Dean&Francis + 509.26 * array[i][3] + 287.37 * array[i][4] + 21070.95 * array[i][5] + 3999.91 * array[i][6] + 193.58 * array[i][7]; eco = 200 * array[i][0] + 50 * array[i][1] - 25 * array[i][2] + 500* array[i][3] - 20 * array[i][4] - 3330 * array[i][5] - 2 * array[i][6] - 2030 * array[i][7]; cout<

Downloads

Published

2023-10-21